/* ===== CRM — monday.com-style board platform ===== */
* { box-sizing: border-box; margin: 0; padding: 0; }
:root {
  --bg: #ffffff;
  --surface: #f6f7fb;
  --ink: #323338;
  --ink-soft: #676879;
  --line: #e6e9ef;
  --line-dark: #d0d4e4;
  --primary: #0073ea;
  --primary-dark: #0060c2;
  --green: #00c875;
  --red: #e2445c;
  --radius: 8px;
  /* Card / modal surface colour, used by everything that needs a step
     above the page background (modals, cards, popovers, table rows).
     Defined here so dark mode can re-tune one variable instead of
     hunting through every #fff in the sheet. */
  --card: #ffffff;
  --shadow: 0 4px 12px rgba(0,0,0,.08);
  --scrollbar: #c9cdda;
}
/* Dark theme — toggled by adding .theme-dark to <html> (set inline by
   layout.php before paint to avoid a flash, then flipped by the
   navbar toggle in app.js, persisted in localStorage). Variables are
   tuned so contrast stays accessible: ink above ~#e0 on a ~#1e bg =
   WCAG AA for body text. */
html.theme-dark,
body.theme-dark {
  --bg: #14161f;
  --surface: #1e2030;
  --ink: #e8eaf2;
  --ink-soft: #8b8fa8;
  --line: #2a2d3f;
  --line-dark: #3a3e54;
  --primary: #4a8eff;
  --primary-dark: #2f72e5;
  --green: #2ad395;
  --red: #f06983;
  --card: #1a1c2a;
  --shadow: 0 4px 14px rgba(0,0,0,.45);
  --scrollbar: #3a3e54;
  color-scheme: dark;
}
html, body { height: 100%; }
body {
  font-family: 'Noto Sans Thai', 'Segoe UI', system-ui, -apple-system, sans-serif;
  background: var(--bg);
  color: var(--ink);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
a { color: inherit; text-decoration: none; }
button { font-family: inherit; cursor: pointer; }
input, select, textarea { font-family: inherit; }
::-webkit-scrollbar { height: 10px; width: 10px; }
::-webkit-scrollbar-thumb { background: var(--scrollbar); border-radius: 6px; }

/* ---------- Share-link passcode prompt ----------
   Shown only when the link's owner set a passcode. Visually distinct from
   the login card so visitors don't mistake it for an account login — no
   gradient backdrop, no email field, explicit "ไม่ต้องล็อกอิน" hint. */
.share-gate-body {
  display: flex; align-items: center; justify-content: center; min-height: 100vh;
  background: #f6f7fb; padding: 16px;
}
.share-gate {
  background: var(--card); padding: 30px 28px; border-radius: 12px;
  border: 1px solid var(--line);
  width: 360px; max-width: calc(100vw - 24px);
  box-shadow: 0 10px 30px rgba(0,0,0,.08);
  text-align: center;
}
.share-gate .sg-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 52px; height: 52px; border-radius: 50%;
  background: rgba(0,115,234,.1); color: var(--primary);
  margin-bottom: 14px;
}
.share-gate h2 { font-size: 18px; margin: 0 0 8px; }
.share-gate .sg-board { margin: 0 0 4px; font-size: 14px; color: var(--ink); }
.share-gate .sg-hint { font-size: 12px; color: var(--ink-soft); margin: 0 0 16px; line-height: 1.5; }
.share-gate .sg-field {
  display: block; text-align: left; font-weight: 600; font-size: 13px;
  margin-bottom: 12px;
}
.share-gate .sg-field input {
  width: 100%; box-sizing: border-box;
  margin-top: 6px; padding: 10px 12px;
  border: 1px solid var(--line-dark); border-radius: 7px; font-size: 14px;
  font-family: inherit;
}
.share-gate .sg-field input:focus { outline: none; border-color: var(--primary); }
.share-gate .sg-submit { width: 100%; justify-content: center; padding: 10px 14px; }

/* ---------- Login ---------- */
.login-body {
  display: flex; align-items: center; justify-content: center; min-height: 100vh;
  background: radial-gradient(circle at 30% 20%, #3d4a8a, #1f2240 70%);
}
.login-card {
  background: var(--card); padding: 40px; border-radius: 14px; width: 380px;
  max-width: calc(100vw - 24px);     /* never overflow narrow phones */
  box-shadow: 0 24px 60px rgba(0,0,0,.35);
}
.login-card .logo-row { font-size: 26px; font-weight: 800; color: var(--primary); margin-bottom: 4px; }
.login-card p { color: var(--ink-soft); margin-bottom: 22px; }
.login-card label { display: block; margin-bottom: 16px; font-weight: 600; }
.login-card input {
  width: 100%; margin-top: 6px; padding: 11px 13px;
  border: 1px solid var(--line-dark); border-radius: var(--radius); font-size: 14px;
}
.login-card input:focus { outline: none; border-color: var(--primary); }
.login-hint { margin-top: 18px; font-size: 12px; color: var(--ink-soft); }
.login-foot { margin-top: 16px; text-align: center; font-size: 13px; }
.login-foot a { color: var(--primary); font-weight: 600; text-decoration: none; }
.login-foot a:hover { text-decoration: underline; }

/* ---------- App shell ---------- */
/* 100dvh = dynamic viewport height: tracks the visible area as the mobile
   browser's toolbar collapses on scroll, so the app fills the screen and
   the toolbar effectively gets out of the way (and is hidden entirely in
   home-screen standalone mode). Falls back to 100vh on older browsers. */
.app { display: flex; flex-direction: column; height: 100vh; height: 100dvh; overflow: hidden; }

/* top navbar */
.navbar {
  /* Light mode = white navbar; dark mode keeps the original navy (overridden
     below). Children read these vars so colours flip with the theme. */
  --nav-fg: var(--ink); --nav-muted: var(--ink-soft);
  --nav-btn: rgba(0,0,0,.05); --nav-btn-hover: rgba(0,0,0,.09);
  height: 48px; flex-shrink: 0; display: flex; align-items: center; gap: 10px;
  padding: 0 12px; background: #fff; color: var(--nav-fg); z-index: 30;
  border-bottom: 1px solid var(--line);
  box-shadow: 0 2px 6px rgba(0,0,0,.06), 0 1px 2px rgba(0,0,0,.04);
}
html.theme-dark .navbar {
  --nav-fg: #fff; --nav-muted: #8b8fb0;
  --nav-btn: rgba(255,255,255,.08); --nav-btn-hover: rgba(255,255,255,.18);
  background: #1d2042; border-bottom-color: transparent;
}
.nav-toggle {
  background: var(--nav-btn); border: none; color: var(--nav-muted);
  width: 30px; height: 30px; border-radius: 6px;
  display: inline-flex; align-items: center; justify-content: center;
}
.nav-toggle:hover { background: var(--nav-btn-hover); color: var(--nav-fg); }
.nav-brand {
  display: flex; align-items: center; gap: 7px; font-size: 14.5px; font-weight: 800; color: var(--nav-fg);
}
.nav-brand .logo { color: #6c8cff; display: inline-flex; align-items: center; }
/* Custom company logo replaces the default icon — fixed height, natural width
   (keeps the logo's aspect ratio; never cropped). Capped so a very wide logo
   can't crowd the navbar. */
.nav-brand .logo img { height: 22px; width: auto; max-width: 160px; border-radius: 4px; object-fit: contain; display: block; }
.nav-spacer { flex: 1; }
/* Navbar board search — type to jump straight to any board the user can reach. */
.nav-search {
  position: relative; display: flex; align-items: center; gap: 7px;
  height: 30px; padding: 0 9px; border-radius: 6px;
  background: var(--nav-btn); color: var(--nav-muted);
  flex: 0 1 280px; max-width: 280px; min-width: 0;
  transition: background .12s;
}
.nav-search:focus-within { background: var(--nav-btn-hover); color: var(--nav-fg); }
.ns-ico { display: inline-flex; flex-shrink: 0; opacity: .85; }
.ns-input {
  flex: 1; min-width: 0; border: none; outline: none; background: transparent;
  color: var(--nav-fg); font-size: 13px; font-family: inherit;
}
.ns-input::placeholder { color: var(--nav-muted); }
.nav-search-results {
  display: none; position: absolute; top: calc(100% + 6px); left: 0;
  width: 340px; max-width: 90vw; max-height: 62vh; overflow-y: auto;
  background: var(--card); color: var(--ink);
  border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 14px 38px rgba(0,0,0,.22); z-index: 60; padding: 4px;
}
.nav-search.ns-open .nav-search-results { display: block; }
.ns-item {
  display: flex; align-items: center; gap: 9px; padding: 8px 10px;
  border-radius: 6px; cursor: pointer; color: var(--ink);
  font-size: 13px; text-decoration: none;
}
.ns-item:hover, .ns-item.ns-active { background: var(--surface); }
.ns-item-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ns-item-name mark { background: rgba(0,115,234,.22); color: inherit; border-radius: 2px; padding: 0 1px; }
.ns-empty { padding: 16px; text-align: center; font-size: 12.5px; color: var(--ink-soft); }
@media (max-width: 768px) {
  /* Collapsed by default — just a tappable search-icon button. */
  .nav-search {
    flex: 0 0 auto; width: 34px; max-width: none; padding: 0;
    justify-content: center; background: rgba(255,255,255,.08);
  }
  .nav-search .ns-input { display: none; }
  .ns-ico { cursor: pointer; }
  /* Tapping the icon expands the field to fill the bar; the wordmark and the
     spacer step aside so the input gets the room. */
  .nav-search.ns-expanded {
    flex: 1 1 auto; width: auto; padding: 0 10px; justify-content: flex-start;
  }
  .nav-search.ns-expanded .ns-input { display: block; }
  .navbar.ns-search-open .nav-brand,
  .navbar.ns-search-open .nav-spacer { display: none; }
  .nav-search-results { position: fixed; top: 56px; left: 8px; right: 8px; width: auto; max-width: none; }
}
.nav-user { display: flex; align-items: center; gap: 8px; }
.nav-user-name { font-size: 12px; line-height: 1.2; }
.nav-user-name small { color: var(--nav-muted); }
.nav-user .cv-avatar { width: 28px; height: 28px; font-size: 11px; }
.nav-logout {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 6px;
  background: var(--nav-btn); color: var(--nav-muted);
}
.nav-logout:hover { background: rgba(226,68,92,.3); color: #fff; }
/* Notification bell — dropdown of unread followup messages. */
.nav-bell {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 6px;
  background: var(--nav-btn); color: var(--nav-muted); cursor: pointer;
}
.nav-bell:hover { background: var(--nav-btn-hover); color: var(--nav-fg); }
.nav-bell-badge {
  position: absolute; top: -3px; right: -3px;
  min-width: 16px; height: 16px; padding: 0 4px;
  border-radius: 8px; background: var(--red); color: #fff;
  font-size: 10px; font-weight: 700; line-height: 16px; text-align: center;
}
.nav-bell-menu {
  position: absolute; top: calc(100% + 8px); right: 0;
  width: 340px; max-width: 90vw;
  background: var(--card); color: var(--ink);
  border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 14px 38px rgba(0,0,0,.22); z-index: 60;
  opacity: 0; pointer-events: none; transform: translateY(-4px);
  transition: opacity .12s ease, transform .12s ease;
  overflow: hidden;
}
.nav-bell.nav-bell-open .nav-bell-menu { opacity: 1; pointer-events: auto; transform: translateY(0); }
/* Mobile — anchor the dropdown to the viewport (fixed, full-width minus
   margins) so it never spills off-screen near the navbar's right edge. */
@media (max-width: 768px) {
  .nav-bell-menu {
    position: fixed; top: 56px; right: 8px; left: 8px;
    width: auto; max-width: none;
  }
}
.nbm-head { padding: 12px 14px; font-weight: 700; font-size: 14px; border-bottom: 1px solid var(--line); }
.nbm-list { max-height: 60vh; overflow-y: auto; }
.nbm-empty { padding: 22px; text-align: center; font-size: 13px; }
.nbm-item {
  display: flex; gap: 10px; padding: 10px 14px; border-bottom: 1px solid var(--line);
  color: var(--ink); text-decoration: none;
}
.nbm-item:hover { background: var(--surface); }
.nbm-av { width: 32px !important; height: 32px !important; font-size: 12px !important; flex-shrink: 0; }
.nbm-body { flex: 1; min-width: 0; }
.nbm-line { font-size: 12.5px; }
.nbm-prev { font-size: 12px; color: var(--ink); margin-top: 1px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nbm-prev.nbm-detail { color: var(--ink-soft); font-size: 11.5px; }
/* Right-aligned type marker — distinguishes followup messages from board
   change alerts at a glance. */
.nbm-type {
  margin-left: auto; align-self: center; flex-shrink: 0;
  display: inline-flex; width: 22px; height: 22px; border-radius: 6px;
  align-items: center; justify-content: center;
}
.nbm-item.nbm-fu .nbm-type    { color: var(--primary); background: rgba(0,115,234,.1); }
.nbm-item.nbm-board .nbm-type { color: #e07a00; background: rgba(245,158,11,.15); }
.nbm-item.nbm-trash .nbm-type { color: #b8253a; background: rgba(226,68,92,.12); }
.nbm-when { font-size: 11px; margin-top: 2px; }

/* Theme toggle — sun shown in light mode, moon in dark, swap via the
   .theme-dark class on <html>. */
.nav-theme {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 6px;
  background: var(--nav-btn); color: var(--nav-muted);
  border: none;
}
.nav-theme:hover { background: var(--nav-btn-hover); color: var(--nav-fg); }
.nav-theme .nt-moon { display: none; }
html.theme-dark .nav-theme .nt-sun  { display: none; }
html.theme-dark .nav-theme .nt-moon { display: inline-flex; }

/* Nav user menu — click the avatar/name on the right of the navbar to
   open a dropdown. The trigger has cursor:pointer; CSS doesn't drive
   open state — JS toggles .nav-user-open. */
.nav-user { position: relative; cursor: pointer; user-select: none; padding: 3px 7px; border-radius: 6px; }
.nav-user:hover { background: var(--nav-btn); }
.nav-user-caret {
  display: inline-flex; color: var(--nav-muted); transition: transform .15s ease;
}
.nav-user.nav-user-open .nav-user-caret { transform: rotate(180deg); }
.nav-user-menu {
  position: absolute; top: calc(100% + 6px); right: 0;
  min-width: 200px;
  background: var(--card); color: var(--ink);
  border: 1px solid var(--line); border-radius: 8px;
  box-shadow: 0 12px 32px rgba(0,0,0,.18);
  padding: 6px; z-index: 60;
  opacity: 0; pointer-events: none; transform: translateY(-4px);
  transition: opacity .12s ease, transform .12s ease;
}
.nav-user.nav-user-open .nav-user-menu {
  opacity: 1; pointer-events: auto; transform: translateY(0);
}
.num-item {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border-radius: 6px;
  color: var(--ink); font-size: 13px; font-weight: 500;
  text-decoration: none; white-space: nowrap;
}
.num-item:hover { background: var(--surface); }
.num-item.num-danger { color: var(--red); }
.num-item.num-danger:hover { background: rgba(226,68,92,.1); }
.num-ico { display: inline-flex; width: 18px; justify-content: center; color: var(--ink-soft); }
.num-item.num-danger .num-ico { color: var(--red); }
.num-sep { height: 1px; background: var(--line); margin: 4px 2px; }
/* Language switcher block inside the user menu. */
.num-head {
  display: flex; align-items: center; gap: 8px;
  padding: 4px 10px 4px; margin-top: 2px;
  font-size: 11px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted);
}
.num-head .num-ico { width: 16px; color: var(--muted); }
.num-item.num-lang { padding: 7px 10px; }
.num-flag { display: inline-flex; width: 18px; justify-content: center; font-size: 15px; line-height: 1; }
.num-item.num-lang .num-lbl { flex: 1; }
.num-item.num-lang.is-active { color: var(--primary); font-weight: 700; }
.num-check { display: inline-flex; color: var(--primary); }

/* Profile page */
.profile-wrap { padding: 18px 32px 60px; max-width: 760px; }
.profile-card {
  background: var(--card); border: 1px solid var(--line); border-radius: 12px;
  padding: 26px;
}
.prof-avatar-row {
  display: flex; align-items: center; justify-content: center; gap: 14px; margin-bottom: 18px;
}
.prof-avatar-pic .cv-avatar { width: 88px; height: 88px; font-size: 30px; }
.prof-avatar-pic .cv-avatar-img { object-fit: cover; }
.prof-pick { cursor: pointer; }
/* Click-the-photo picker (mirrors company.php's cs-logo-pick) — camera badge,
   and an X on hover to remove. Size unchanged (the 64px .cv-avatar above). */
.prof-avatar-pick { position: relative; width: 88px; height: 88px; cursor: pointer; border-radius: 50%; transition: box-shadow .12s ease; }
.prof-avatar-pick:hover { box-shadow: 0 0 0 3px rgba(0,115,234,.18); }
.prof-avatar-prev { display: inline-flex; width: 100%; height: 100%; }
.prof-avatar-edit {
  position: absolute; right: 0; bottom: 0; width: 24px; height: 24px; border-radius: 50%;
  background: var(--primary); color: #fff; display: inline-flex; align-items: center; justify-content: center;
  border: 2px solid var(--card); pointer-events: none;
}
.prof-avatar-edit .icon { width: 13px; height: 13px; }
.prof-avatar-x {
  position: absolute; right: -2px; top: -2px; width: 24px; height: 24px; padding: 0; border-radius: 50%;
  background: var(--red); color: #fff; border: 2px solid var(--card); cursor: pointer;
  display: none; align-items: center; justify-content: center;
}
.prof-avatar-x .icon { width: 13px; height: 13px; }
.prof-avatar-pick.has-avatar:hover .prof-avatar-x    { display: inline-flex; }
.prof-avatar-pick.has-avatar:hover .prof-avatar-edit { display: none; }
.prof-pick input[type="file"] { display: none; }
.prof-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 12px 16px;
}
.prof-field { display: flex; flex-direction: column; gap: 4px; font-size: 13px; font-weight: 600; min-width: 0; }
.prof-field-wide { grid-column: 1 / -1; }
.prof-field input, .prof-field textarea {
  padding: 8px 10px; border: 1px solid var(--line); border-radius: 6px;
  background: var(--card); color: var(--ink); font-family: inherit; font-size: 13px;
  font-weight: 400;
}
.prof-field input:focus, .prof-field textarea:focus {
  outline: none; border-color: var(--primary); box-shadow: 0 0 0 2px rgba(0,115,234,.18);
}
.prof-field input:disabled { background: var(--surface); color: var(--ink-soft); cursor: not-allowed; }
.prof-pwsec {
  border: 1px solid var(--line); border-radius: 8px;
  padding: 12px 14px 14px; margin-top: 18px;
}
.prof-pwsec legend { font-size: 13px; font-weight: 700; padding: 0 6px; color: var(--ink); }
.prof-actions { margin-top: 20px; display: flex; justify-content: flex-end; }
@media (max-width: 640px) {
  .profile-wrap { padding: 14px 8px 40px; }
  .profile-card { padding: 18px 14px; }
  .prof-grid { grid-template-columns: 1fr; }
}

.app-body { flex: 1; display: flex; overflow: hidden; }

/* Board fullscreen — toggled by the 3-dot menu. Hides the top navbar and
   left sidebar so the board area takes the full viewport. ESC exits. */
body.board-fullscreen .navbar,
body.board-fullscreen .sidebar { display: none; }
body.board-fullscreen .app-body { height: 100vh; }

/* Share-link guest mode — anonymous visitor accessed via a share URL.
   Strip the app chrome entirely: no navbar, no sidebar, just the board.
   The visitor came in for one board and can't navigate anywhere else, so
   showing app shell would be misleading. The board area expands to fill
   the viewport like in fullscreen mode. */
body.is-guest .navbar,
body.is-guest .sidebar { display: none !important; }
body.is-guest .app-body { height: 100vh; }
body.is-guest .main { width: 100%; }
/* Even if the share link granted "edit", guests are not board managers —
   strip every schema/structure control so they can only touch cell data
   (and add items, if the link allows that). */
body.is-guest #btn-board-menu,
body.is-guest #btn-board-icon,
body.is-guest .view-add,
body.is-guest .view-menu,
body.is-guest .col-menu,
body.is-guest .btn-add-col,
body.is-guest .col-add-th,
body.is-guest .g-menu,
body.is-guest .g-color,
body.is-guest #btn-add-group,
body.is-guest .dw-menu,
body.is-guest #dash-add,
body.is-guest .cal-settings-btn { display: none !important; }
body.is-guest #board-name { cursor: default; }

/* Board permission gating — body classes set by app.js bootstrap. Hides
   every control that would otherwise post to an API the user can't act
   on (and saves a round-trip to a 403). The backend always enforces
   too — this is purely UX. */
body.bp-no-edit .cal-settings-btn,
body.bp-no-edit #btn-board-icon,
body.bp-no-edit #btn-board-menu,
body.bp-no-edit #btn-add-view,
body.bp-no-edit .view-add,
body.bp-no-edit .view-menu,
body.bp-no-edit .col-menu,
body.bp-no-edit .btn-add-col,
body.bp-no-edit .col-add-th,
body.bp-no-edit #btn-add-group,
body.bp-no-edit .g-menu,
body.bp-no-edit .dw-menu,
body.bp-no-edit #dash-add,
body.bp-no-edit .board-fab,
body.bp-no-edit .g-color { display: none !important; }
/* Non-edit users can still see contenteditable surfaces but the cursor
   shouldn't suggest they can type into them. JS also strips the attr. */
body.bp-no-edit #board-name,
body.bp-no-edit .g-title,
body.bp-no-edit .th-name { cursor: default; }

/* No add permission → hide every "add a new row / subitem" affordance.
   Edit-only users (e.g. cleanup roles) can still rename/edit existing
   content even without add. */
body.bp-no-add #btn-new-item,
body.bp-no-add .add-row,
body.bp-no-add .sub-add-row,
body.bp-no-add .add-subitem-input,
body.bp-no-add .add-item-input { display: none !important; }

/* No delete permission → hide the cell-level file remove "x". The menu
   delete options are gated in JS (each .opt is built conditionally so
   the popover only renders the entry when canDelete()). */
body.bp-no-delete .cv-file-x { display: none !important; }

/* sidebar */
.sidebar {
  /* Light mode = soft light sidebar; dark mode keeps the navy gradient
     (overridden below). Children read these vars so colours flip with theme. */
  --sb-fg: #4b5070;
  --sb-hover-bg: rgba(0,0,0,.05); --sb-hover-fg: var(--ink);
  --sb-active-bg: rgba(0,115,234,.10); --sb-active-fg: var(--primary);
  --sb-muted: var(--ink-soft); --sb-chip-bg: rgba(0,0,0,.06);
  width: 248px; flex-shrink: 0; color: var(--sb-fg); display: flex; flex-direction: column;
  background: linear-gradient(180deg, #fbfcfe 0%, #f1f3f8 100%);
  border-right: 1px solid var(--line);
  transition: width .18s ease; overflow: hidden;
}
html.theme-dark .sidebar {
  --sb-fg: #c9cce0;
  --sb-hover-bg: rgba(255,255,255,.08); --sb-hover-fg: #fff;
  --sb-active-bg: rgba(108,140,255,.28); --sb-active-fg: #fff;
  --sb-muted: #7a7e9e; --sb-chip-bg: rgba(255,255,255,.08);
  background: linear-gradient(180deg, #2a2e54 0%, #1d2042 100%);
  border-right-color: transparent;
}
.side-link {
  display: flex; align-items: center; gap: 9px; margin: 2px 10px;
  padding: 7px 12px; border-radius: 7px; color: var(--sb-fg); font-weight: 500;
  font-size: 11.5px; white-space: nowrap;
}
.side-link:hover { background: var(--sb-hover-bg); color: var(--sb-hover-fg); }
.side-link.active { background: var(--sb-active-bg); color: var(--sb-active-fg); }
.side-link .ico { display: inline-flex; width: 18px; justify-content: center; flex-shrink: 0; }
.side-link .ico .icon,
.side-folder-head .ico .icon { width: 13px; height: 13px; }
.side-link .side-label { overflow: hidden; text-overflow: ellipsis; }
.side-section {
  display: flex; justify-content: space-between; align-items: center;
  padding: 16px 18px 6px; font-size: 11px; letter-spacing: .08em; color: var(--sb-muted);
}
.side-section button, .side-add-btn {
  background: var(--sb-chip-bg); border: none; color: var(--sb-hover-fg); width: 24px; height: 24px;
  border-radius: 6px; display: inline-flex; align-items: center; justify-content: center;
}
.side-section button:hover, .side-add-btn:hover { background: var(--primary); color: #fff; }
.board-nav { flex: 1; overflow-y: auto; overflow-x: hidden; }
.side-top { padding-top: 10px; }
.side-empty { padding: 8px 22px; color: var(--sb-muted); font-size: 12px; }

/* collapsed sidebar — icon rail */
.app.nav-collapsed .sidebar { width: 64px; }
.app.nav-collapsed .side-label,
.app.nav-collapsed .side-section span,
.app.nav-collapsed .side-empty { display: none; }
.app.nav-collapsed .side-link { justify-content: center; padding-left: 0; padding-right: 0; }
.app.nav-collapsed .side-sublink { margin-left: 10px; }
.app.nav-collapsed .side-section { justify-content: center; padding: 14px 0 6px; }
.app.nav-collapsed .side-folder-head { display: none; }
.app.nav-collapsed .side-folder-boards { display: block !important; }
.app.nav-collapsed .side-section-actions { display: none; }

/* folders in sidebar */
.side-section-actions { display: inline-flex; gap: 6px; }
.side-folder-head {
  display: flex; align-items: center; gap: 8px; margin: 2px 10px;
  padding: 7px 10px; border-radius: 7px; color: var(--sb-fg); font-weight: 600;
  font-size: 11.5px; cursor: pointer; white-space: nowrap;
}
.side-folder-head:hover { background: var(--sb-hover-bg); color: var(--sb-hover-fg); }
.sf-caret { display: inline-flex; transition: transform .15s ease; flex-shrink: 0; }
.side-folder.collapsed .sf-caret { transform: rotate(-90deg); }
.side-folder.collapsed .side-folder-boards { display: none; }
.sf-name { flex: 1; overflow: hidden; text-overflow: ellipsis; }
.sf-count {
  font-size: 11px; color: var(--sb-muted); background: var(--sb-chip-bg);
  border-radius: 9px; padding: 1px 7px; font-weight: 600;
}
.sf-menu { opacity: 0; display: inline-flex; color: var(--sb-muted); }
.side-folder-head:hover .sf-menu { opacity: 1; }
.sf-menu:hover { color: var(--sb-hover-fg); }
/* "ถูกแชร์" category — boards shared to a member, grouped like a folder but
   with a share glyph tinted to set it apart from their own folders. The tint is
   scoped to the section header only (direct child) so nested folders inside it
   keep a normal folder glyph. */
.side-shared > .side-folder-head > .ico { color: #6c8cff; }
/* A shared board kept inside its owner's folder is shown as a folder nested one
   level deeper inside the แชร์ section. */
.side-shared > .side-folder-boards > .side-shared-sub { margin-left: 8px; }
.side-sublink { margin-left: 22px; }
.side-folder-boards .side-empty { padding: 6px 14px; }

/* Board name shrinks so the 3-dot menu has room to sit on the right edge. */
.sb-name { flex: 1; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
/* 3-dot ⋯ menu on each sidebar board — hidden until hover, like the
   folder menu. Sits right-aligned at the end of the row. */
.sb-menu {
  opacity: 0; display: inline-flex; flex-shrink: 0;
  color: var(--sb-muted); padding: 0 2px; border-radius: 4px;
  margin-left: auto;
}
.side-link:hover .sb-menu { opacity: 1; }
.sb-menu:hover { color: var(--sb-hover-fg); background: var(--sb-hover-bg); }

/* Admin-only — group sidebar boards by their creator. Each user gets a
   collapsible header that contains their folders + standalone boards. */
.side-user-head {
  display: flex; align-items: center; gap: 8px; margin: 10px 10px 4px;
  padding: 6px 10px; border-radius: 7px;
  color: var(--sb-hover-fg); font-weight: 700; font-size: 12px;
  background: var(--sb-chip-bg);
  text-transform: uppercase; letter-spacing: .02em;
  cursor: pointer; white-space: nowrap;
}
.side-user-head:hover { background: var(--sb-hover-bg); }
.su-caret { display: inline-flex; transition: transform .15s ease; flex-shrink: 0; }
.side-user.collapsed .su-caret { transform: rotate(-90deg); }
.side-user.collapsed .side-user-content { display: none; }
.su-name { flex: 1; overflow: hidden; text-overflow: ellipsis; }
.su-count {
  font-size: 11px; color: var(--sb-muted); background: var(--sb-chip-bg);
  border-radius: 9px; padding: 1px 7px; font-weight: 600;
}
/* Indent everything inside a user section so the hierarchy reads at a glance */
.side-user-content > .side-folder { margin-left: 6px; }
.side-user-content > .side-link    { margin-left: 16px; }
.app.nav-collapsed .side-user-head { display: none; }

/* popover head / separator */
.pop-head { font-size: 11px; font-weight: 700; color: var(--ink-soft); padding: 3px 8px 2px; }
.pop-sep { height: 1px; background: var(--line); margin: 3px 0; }

.main { flex: 1; overflow-y: auto; display: flex; flex-direction: column; }

/* ---------- Alerts ---------- */
.alert { margin: 14px 28px 0; padding: 11px 14px; border-radius: var(--radius); font-weight: 500; }
.alert-success { background: #e6f8f0; color: #027a48; }
.alert-error { background: #fdeaec; color: #b42318; }

/* ---------- Generic ---------- */
.btn {
  display: inline-flex; align-items: center; gap: 6px; padding: 9px 16px;
  border-radius: var(--radius); border: 1px solid var(--line-dark);
  background: var(--card); color: var(--ink); font-weight: 600; font-size: 13px;
}
.btn:hover { background: var(--surface); }
.btn-primary { background: var(--primary); border-color: var(--primary); color: #fff; }
.btn-primary:hover { background: var(--primary-dark); }
.btn-sm { padding: 6px 10px; font-size: 12px; }
.muted { color: var(--ink-soft); }

/* ---------- Home / board list ---------- */
.page-head { padding: 26px 32px 6px; }
.page-head h1 { font-size: 24px; }
.page-head p { color: var(--ink-soft); }
.board-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 18px; padding: 22px 32px;
}
.board-card {
  background: var(--card); border: 1px solid var(--line); border-radius: 12px;
  padding: 18px; transition: .12s; position: relative;
}
.board-card:hover { border-color: var(--primary); box-shadow: 0 6px 20px rgba(0,115,234,.12); }
.board-card .bc-icon { font-size: 28px; }
.board-card .bc-head {
  display: flex; align-items: center; gap: 8px;
}
/* Creator chip is the first item after the icon — margin-left:auto
   pushes it (and everything after it — the pin button) to the right
   edge, leaving the icon alone on the left. */
.board-card .bc-creator { margin-left: auto; }
.board-card .bc-creator {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11px; color: var(--ink-soft); font-weight: 500;
  background: var(--surface); border-radius: 11px; padding: 3px 9px;
  max-width: 60%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.board-card .bc-creator .icon { width: 12px; height: 12px; flex-shrink: 0; }
.board-card h3 { margin: 10px 0 4px; font-size: 16px; }
.board-card .bc-meta { color: var(--ink-soft); font-size: 12px; }
.board-card .bc-bar { height: 4px; border-radius: 4px; margin-top: 14px; background: var(--primary); }
/* Hidden-from-sidebar card — slightly dimmed so the state is visible at
   a glance on boards.php without competing for attention. */
.board-card.bc-sidebar-hidden { opacity: .78; }
.board-card.bc-sidebar-hidden:hover { opacity: 1; }
.bc-hidden-badge {  /* legacy — kept in case any other view still references it */
  display: inline-flex; align-items: center; gap: 3px;
  margin-left: 8px;
  font-size: 10.5px; font-weight: 600; color: #b8253a;
  background: rgba(226,68,92,.1); border-radius: 9px; padding: 1px 7px;
}
.bc-hidden-badge .icon { width: 11px; height: 11px; }

/* Pin toggle on each board card. Sits right after the creator chip;
   the creator's margin-left:auto already shoves both of them to the
   right edge, so no extra positioning needed here. */
.bc-pin {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; flex-shrink: 0;
  background: transparent; border: 1px solid transparent;
  border-radius: 6px; color: var(--ink-soft); cursor: pointer;
  transition: background .12s, color .12s, border-color .12s;
}
.bc-pin:hover { background: var(--surface); color: var(--ink); }
.bc-pin.bc-pin-on { color: var(--primary); }
.bc-pin.bc-pin-on:hover { background: rgba(0,115,234,.1); border-color: rgba(0,115,234,.3); }
/* Share-link badges — open = blue (anyone with the URL), pass = amber
   (URL requires a code). Sits in the meta row beside item count. */
.bc-share-badge {
  display: inline-flex; align-items: center; gap: 3px;
  margin-left: 8px;
  font-size: 10.5px; font-weight: 600;
  border-radius: 9px; padding: 1px 7px;
}
.bc-share-badge .icon { width: 11px; height: 11px; }
.bc-share-open { color: #0060c2; background: rgba(0,115,234,.1); }
.bc-share-pass { color: #a96f00; background: rgba(255,176,0,.12); }

/* Folder grouping on the boards landing page. Each group has a clickable
   header (caret + folder icon + name + count) that toggles the body. */
.folder-group { padding: 0 32px; margin-top: 8px; }
.folder-group:first-of-type { margin-top: 4px; }
.folder-group-head {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 14px; margin-top: 14px;
  background: var(--card); border: 1px solid var(--line); border-radius: 10px;
  cursor: pointer; user-select: none;
  transition: background .12s;
}
.folder-group-head:hover { background: var(--surface); }
.fg-caret { display: inline-flex; transition: transform .15s ease; color: var(--ink-soft); flex-shrink: 0; }
.folder-group.collapsed .fg-caret { transform: rotate(-90deg); }
.folder-group.collapsed .folder-group-body { display: none; }
.folder-group.collapsed .folder-group-head { margin-bottom: 0; }
.fg-icon  { display: inline-flex; color: var(--primary); flex-shrink: 0; }
.fg-name  { flex: 1; font-size: 15px; font-weight: 700; color: var(--ink); }
.fg-count {
  font-size: 11px; color: var(--ink-soft); background: var(--surface);
  border-radius: 10px; padding: 2px 9px; font-weight: 600;
}
.fg-actions { display: inline-flex; align-items: center; gap: 6px; }
.folder-group-body .board-grid { padding: 14px 0 4px; }
.folder-group-body .empty-state { background: transparent; }

/* ---------- Board view ---------- */
.board-top {
  padding: 20px 32px 0; border-bottom: 1px solid var(--line); background: var(--card);
  position: sticky; top: 0; z-index: 20;
}
.board-title { display: flex; align-items: center; gap: 10px; }
.board-title .b-icon { font-size: 26px; }
.board-title h1 {
  font-size: 22px; border: 1px solid transparent; border-radius: 6px; padding: 2px 6px;
}
.board-title h1:focus { outline: none; }
.board-menu-btn {
  border: none; background: transparent; color: var(--ink-soft);
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 6px;
}
.board-menu-btn:hover { background: var(--surface); color: var(--ink); }
/* Right-aligned ownership / share indicator on the board title row */
.board-share-ind, .board-owner { margin-left: auto; flex-shrink: 0; }
.board-share-ind {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 8px;
  color: #6c8cff; background: var(--surface);
}
.board-owner { border: none; background: transparent; padding: 0; cursor: pointer; display: inline-flex; }
.board-owner .cv-avatar.bo-av { width: 30px; height: 30px; font-size: 12px; }
.board-owner:hover .cv-avatar { filter: brightness(1.08); }
.bo-pop {
  position: fixed; z-index: 1200; min-width: 220px; max-width: 300px;
  background: var(--card); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: 0 10px 30px rgba(0,0,0,.2); padding: 14px;
}
.bo-pop-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.bo-pop-avatar {
  width: 40px; height: 40px; border-radius: 50%; flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--primary); color: #fff; font-weight: 700; font-size: 15px;
}
img.bo-pop-avatar { object-fit: cover; }
.bo-pop-name { font-weight: 700; color: var(--ink); font-size: 14px; }
.bo-pop-role { font-size: 12px; color: var(--ink-soft); margin-top: 1px; }
.bo-pop-row {
  display: flex; align-items: center; gap: 8px; font-size: 13px;
  padding-top: 9px; border-top: 1px solid var(--line);
}
.bo-pop-lbl { color: var(--ink-soft); font-weight: 600; min-width: 38px; }
.bo-pop-email { color: var(--primary); text-decoration: none; word-break: break-all; }
.bo-pop-email:hover { text-decoration: underline; }
/* Per-user "alert me about changes" toggle. Muted (outline bell) when off,
   filled in the brand colour when subscribed. */
.board-alert-btn {
  border: none; background: transparent; color: var(--ink-soft); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 6px;
}
.board-alert-btn:hover { background: var(--surface); color: var(--ink); }
.board-alert-btn.on { color: var(--primary); }
.board-alert-btn.on svg { fill: var(--primary); fill-opacity: .18; }
/* Robot/integration toggle — same chrome as the other header icon buttons. */
.board-integrate-btn {
  border: none; background: transparent; color: var(--ink-soft); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 6px;
}
.board-integrate-btn:hover { background: var(--surface); color: var(--ink); }
/* ---- Integration (robot) modal ---- */
.intg-modal { width: 480px; max-width: 94vw; }
.intg-title { font-size: 17px; font-weight: 800; margin: 0; }
.intg-lead { font-size: 12.5px; margin: 2px 0 16px; }
/* Each integration is a card with a brand chip header. */
.intg-card {
  border: 1px solid var(--line); border-radius: 12px; padding: 14px;
  margin-bottom: 12px; background: var(--card);
}
.intg-card-top { display: flex; align-items: center; gap: 11px; }
.intg-card-meta { flex: 1; min-width: 0; }
.intg-card-title { font-size: 14px; font-weight: 700; }
.intg-card-sub { font-size: 12px; color: var(--ink-soft); margin-top: 1px; line-height: 1.4; }
.intg-brand {
  flex-shrink: 0; width: 38px; height: 38px; border-radius: 10px;
  display: inline-flex; align-items: center; justify-content: center;
}
.intg-brand-discord { background: #5865f2; color: #fff; }
.intg-brand-google  { background: #fff; border: 1px solid var(--line); }
/* Google account card — email + app-password inputs reuse .intg-dc-field for
   the leading-icon input styling. The fields are DIRECT children of .intg-card
   (Discord's live deeper inside .intg-dc-list) so `>` scopes the gap to Google. */
.intg-card > .intg-dc-field { margin-top: 10px; }
.intg-gg-hint { font-size: 11.5px; margin: 8px 0 0; line-height: 1.45; }
.intg-gg-link { color: var(--primary); font-weight: 600; text-decoration: underline; }
.intg-gg-link:hover { opacity: .85; }
/* Discord webhook rows — name + URL stacked (each with a leading icon),
   delete on the right. Row lifts on focus. */
.intg-dc-list { display: flex; flex-direction: column; gap: 10px; margin: 12px 0 10px; }
.intg-dc-row {
  display: flex; flex-direction: column; gap: 8px;
  padding: 12px; border: 1px solid var(--line); border-radius: 11px; background: var(--card);
  box-shadow: 0 1px 3px rgba(0,0,0,.05);
  transition: border-color .12s, box-shadow .12s;
}
.intg-dc-row .cs-select { background: var(--bg); }
.intg-dc-row:focus-within { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(0,115,234,.12); }
.intg-dc-field { position: relative; }
.intg-dc-fic {
  position: absolute; left: 9px; top: 50%; transform: translateY(-50%);
  display: inline-flex; color: var(--ink-soft); pointer-events: none;
}
.intg-dc-fic svg { width: 14px; height: 14px; }
/* Scoped under .intg-modal so it beats the generic `.cs-set-modal .cs-select`
   padding rule (same specificity, but that one is later in the file). */
.intg-modal .intg-dc-field .cs-select { width: 100%; padding-left: 32px; }
.intg-modal .intg-dc-field .intg-dc-label { font-weight: 600; }
.intg-modal .intg-dc-field .intg-dc-url { font-size: 12px; }
/* Footer = an input-group of two equal-width buttons: an ใช้งาน toggle
   (grey = off, green = on) joined to a delete button. */
.intg-dc-foot { display: flex; margin-top: 3px; }
.intg-dc-foot > button {
  flex: 1; min-width: 0; position: relative;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: 34px; border: 1px solid var(--line); background: var(--card);
  color: var(--ink-soft); cursor: pointer; font-size: 13px; font-weight: 600; font-family: inherit;
  transition: background .12s, color .12s, border-color .12s;
}
.intg-dc-foot > button svg { width: 15px; height: 15px; }
.intg-dc-on  { border-radius: 8px 0 0 8px; }
.intg-dc-del { border-radius: 0 8px 8px 0; margin-left: -1px; }
.intg-dc-on.is-on { background: rgba(0,200,117,.14); color: #00a35e; border-color: rgba(0,200,117,.55); z-index: 1; }
.intg-dc-on.is-on svg { color: #00a35e; }
.intg-dc-del:hover { background: rgba(226,68,92,.1); color: var(--red); border-color: rgba(226,68,92,.45); z-index: 1; }
/* Gmail card footer: ทดสอบ (left) + ลบ (right) — same two-button group shape. */
.intg-gg-test { border-radius: 8px 0 0 8px; color: var(--primary); }
.intg-gg-test:hover { background: rgba(0,115,234,.08); color: var(--primary); border-color: var(--primary); z-index: 1; }
.intg-gg-del  { border-radius: 0 8px 8px 0; margin-left: -1px; }
.intg-gg-del:hover { background: rgba(226,68,92,.1); color: var(--red); border-color: rgba(226,68,92,.45); z-index: 1; }
.intg-gg-test svg, .intg-gg-del svg { width: 15px; height: 15px; }
/* Dashed "add webhook" button spanning the card. */
.intg-dc-add {
  width: 100%; display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 9px; border: 1px dashed var(--line-dark); border-radius: 9px;
  background: transparent; color: var(--primary); cursor: pointer;
  font-size: 13px; font-weight: 600; font-family: inherit;
}
.intg-dc-add:hover { background: rgba(0,115,234,.06); border-color: var(--primary); }
.intg-dc-add svg { width: 15px; height: 15px; }

/* Per-column integration picker — checkbox list of the board's webhooks. */
/* Scheduled-send (time-based integration) modal */
.csch-modal { width: 440px; max-width: 94vw; }
.csch-modal .csch-title { display: flex; align-items: center; gap: 7px; }
.csch-modal .csch-title svg { width: 18px; height: 18px; flex-shrink: 0; }
.csch-modal .csch-sec { border: 1px solid var(--line); border-radius: 10px; padding: 9px 12px 11px; margin-bottom: 10px; }
.csch-modal .csch-line { display: flex; align-items: center; gap: 7px; font-size: 13px; margin: 7px 0; flex-wrap: wrap; }
.csch-modal .csch-head { margin-top: 0; }
.csch-modal .csch-line .cs-select { margin-left: 4px; flex: 1; min-width: 140px; }

.colintg-modal { width: 420px; max-width: 94vw; }
.colintg-modal .intg-title { display: flex; align-items: center; gap: 7px; }
.colintg-modal .intg-title svg { width: 18px; height: 18px; }
.colintg-list { display: flex; flex-direction: column; gap: 8px; margin: 6px 0 4px; }
.colintg-row {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; border: 1px solid var(--line); border-radius: 9px;
  cursor: pointer; background: var(--card); transition: border-color .12s, background .12s;
}
.colintg-row:hover { border-color: var(--primary); background: rgba(0,115,234,.04); }
.colintg-row .colintg-cb { width: 16px; height: 16px; accent-color: var(--primary); cursor: pointer; }
.colintg-brand { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; flex: none; }
.colintg-brand svg { width: 18px; height: 18px; }
.colintg-brand-discord { color: #5865F2; }
.colintg-name { flex: 1; font-size: 13px; font-weight: 600; }
/* A disabled (board-wide) integration is still selectable here and looks
   identical to an enabled one — only the "ปิดอยู่" tag marks it. */
.colintg-offtag {
  font-size: 11px; font-weight: 600; color: var(--muted);
  background: var(--bg); border: 1px solid var(--line); border-radius: 999px; padding: 1px 8px;
}
.colintg-empty { padding: 14px 4px; font-size: 13px; text-align: center; }
.b-icon {
  border: 1px solid transparent; background: transparent; color: var(--primary);
  border-radius: 7px; padding: 3px; cursor: pointer;
}
.b-icon:hover { border-color: var(--line-dark); background: var(--surface); }
.icon-grid {
  display: grid; grid-template-columns: repeat(6, 1fr); gap: 2px;
  max-height: 280px; overflow-y: auto;
}
.ic-opt {
  display: flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; border-radius: 7px; cursor: pointer; color: var(--ink-soft);
}
.ic-opt:hover { background: var(--surface); color: var(--primary); }
.board-desc { color: var(--ink-soft); font-size: 13px; margin: 4px 0 14px; }
/* Horizontally scrollable when there are more views than the row can fit.
   Scrollbar hidden — drag-to-scroll wired in app.js (same pattern as the
   gallery thumb strip). */
.view-tabs {
  display: flex; gap: 2px; align-items: center; flex-wrap: nowrap;
  overflow-x: auto; overflow-y: hidden;
  scrollbar-width: none; -ms-overflow-style: none;
  cursor: grab; user-select: none;
}
.view-tabs::-webkit-scrollbar { display: none; }
.view-tabs.dragging { cursor: grabbing; }
.view-tabs > * { flex-shrink: 0; }
/* Reorder-drag feedback — owner/admin only, draggable=true on each tab.
   The dragged element fades out so the user can see where it'll land. */
.view-tabs[data-can-reorder="1"] .view-tab { cursor: grab; }
.view-tabs[data-can-reorder="1"] .view-tab:active { cursor: grabbing; }
.view-tab-dragging { opacity: .35; }
/* Home dashboard tab: the icon is clickable to change it (data-page-icon). */
.home-page-tabs .page-ico {
  display: inline-flex; align-items: center; cursor: pointer;
  border-radius: 6px; padding: 2px; margin: -2px 0; transition: background .12s ease;
}
.home-page-tabs .page-ico:hover { background: rgba(0,0,0,.10); }
/* Table view column reorder — owner/admin sees grab cursor on data
   column headers (the name column has no data-col and isn't draggable
   so it stays fixed at the start). */
.tbl thead th[draggable="true"] { cursor: grab; }
.tbl thead th[draggable="true"]:active { cursor: grabbing; }
.th-dragging { opacity: .4; }
.view-tabs a {
  padding: 8px 14px; font-weight: 600; font-size: 13px; color: var(--ink-soft);
  border-bottom: 3px solid transparent;
  white-space: nowrap;
}
.view-tabs a.active { color: var(--ink); border-bottom-color: var(--primary); }
/* Gmail view tab — tint it Gmail red so it stands out from the other views.
   Scoped under `.view-tabs a` so each rule outranks the base/active rules above. */
.view-tabs a[data-vtype="gmail"] { color: #d93025; }
.view-tabs a[data-vtype="gmail"] .icon { color: #ea4335; }
.view-tabs a[data-vtype="gmail"]:hover { color: #ea4335; }
.view-tabs a.active[data-vtype="gmail"] { color: #d93025; border-bottom-color: #ea4335; }
/* join_board settings — Gmail integrate rows for joined email columns. */
.jb-gmail-list { display: flex; flex-direction: column; gap: 6px; }
.jb-gm-row { display: flex; align-items: center; gap: 10px; padding: 7px 10px; border: 1px solid var(--line); border-radius: 8px; background: var(--bg); }
.jb-gm-name { font-weight: 600; font-size: 13px; }
.jb-gm-on  { font-size: 12px; color: #2e7d32; }
.jb-gm-off { font-size: 12px; }
.jb-gm-row .jb-gm-btn { margin-left: auto; }
.view-menu { cursor: pointer; opacity: .55; display: inline-flex; margin-left: 2px; }
.view-menu:hover { opacity: 1; }
.view-add {
  border: none; background: transparent; color: var(--ink-soft);
  font-size: 12.5px; font-weight: 600; padding: 8px 12px;
  display: inline-flex; align-items: center; gap: 5px; border-radius: 6px;
}
.view-add:hover { background: var(--surface); color: var(--primary); }
/* Home dashboard page tabs — the .view-tabs bar reused on the home page; it is
   the page header now (no greeting block). It sits in a sticky .page-head that
   mirrors the board's .board-top: pinned to the top of the scroll area with an
   opaque background + a full-width bottom divider, so the tabs stay visible
   while the cards scroll. The two-class selector outranks .page-head's own
   padding (incl. its mobile override). Settings / rename / delete live in the
   active tab's ⋯ menu. */
.page-head.home-tabs-head {
  padding: 20px 32px 0; border-bottom: 1px solid var(--line); background: var(--card);
  position: sticky; top: 0; z-index: 20;
}
.board-actions { display: flex; gap: 8px; padding: 14px 32px; align-items: center; }

/* ── Board-level filter bar — flat horizontal form ────────────────────
   One light card containing label/input columns flowing left-to-right.
   Action buttons (ค้นหา, ล้างทั้งหมด) sit at the right edge of the row.
   Active filters are added/removed via the ⋯ menu. */
.filter-bar {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: 14px;
  margin: 10px 32px 0; padding: 12px 16px;
  background: var(--surface); border: 1px solid var(--line); border-radius: 8px;
}
.filter-bar:empty { display: none; }
.filter-field {
  display: flex; flex-direction: column; gap: 4px;
  flex: 1 1 180px; min-width: 0;
}
.filter-field .fr-col {
  font-size: 11.5px; font-weight: 600; color: var(--ink);
}
.filter-field .fr-ed { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
.filter-field .fr-input {
  padding: 5px 8px; border: 1px solid var(--line); border-radius: 5px;
  font-family: inherit; font-size: 12px; background: var(--card); width: 100%;
  box-sizing: border-box;
}
.filter-field .fr-input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 2px rgba(87,155,252,.18); }
.filter-field .fr-num { flex: 1 1 70px; width: auto; }
.filter-field .fr-date { flex: 1 1 130px; width: auto; }

/* Bootstrap-style connected input group for range filters (date, number) */
.filter-field .fr-group {
  display: flex; align-items: stretch; width: 100%;
}
.filter-field .fr-group > * { border: 1px solid var(--line); }
.filter-field .fr-group > .fr-input {
  border-radius: 0; min-width: 0; flex: 1;
}
.filter-field .fr-group > .fr-sep {
  display: inline-flex; align-items: center; padding: 0 10px;
  background: var(--surface); color: var(--ink-soft);
  font-size: 12px; white-space: nowrap;
  border-left: none; border-right: none;
}
.filter-field .fr-group > *:first-child { border-radius: 6px 0 0 6px; }
.filter-field .fr-group > *:last-child  { border-radius: 0 6px 6px 0; }
.filter-field .fr-group > .fr-input:focus {
  z-index: 1; box-shadow: 0 0 0 2px rgba(87,155,252,.18);
}

.filter-field .fr-chip {
  border: 1px solid transparent; color: #fff; padding: 4px 10px;
  border-radius: 12px; font-size: 11.5px; font-weight: 600; cursor: pointer;
  opacity: .55; transition: opacity .1s ease;
}
.filter-field .fr-chip:hover { opacity: .85; }
.filter-field .fr-chip.on { opacity: 1; box-shadow: 0 0 0 2px rgba(0,0,0,.18) inset; }
.filter-field .fr-people {
  background: #e6efff; color: var(--ink); opacity: 1; border: 1px solid var(--line);
}
.filter-field .fr-people.on { background: var(--primary); color: #fff; border-color: var(--primary); box-shadow: none; }
.filter-field .fr-select { cursor: pointer; appearance: auto; }

/* Custom dropdown widget — used by both filter bar and form view.
   Scoped to the bare class so it works anywhere with a .fr-csel container. */
.fr-csel { position: relative; width: 100%; }
.fr-csel-btn {
  display: flex; align-items: center; justify-content: space-between;
  width: 100%; padding: 5px 8px; font-size: 12px; font-family: inherit;
  border: 1px solid var(--line); border-radius: 5px; background: var(--card);
  color: var(--ink); cursor: pointer; text-align: left;
}
.fr-csel-lbl { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fr-csel-caret { color: var(--ink-soft); font-size: 10px; margin-left: 6px; }
.fr-csel-pop {
  position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: 50;
  background: var(--card); border: 1px solid var(--line); border-radius: 6px;
  box-shadow: 0 6px 24px rgba(0,0,0,.12); padding: 4px; max-height: 280px; overflow-y: auto;
}
.fr-csel-opt {
  padding: 6px 10px; border-radius: 4px; cursor: pointer; font-size: 12px;
  margin-bottom: 2px; transition: filter .1s ease;
}
.fr-csel-opt:last-child { margin-bottom: 0; }
.fr-csel-opt:hover { filter: brightness(0.92); }
.fr-csel-opt-all { background: var(--surface); color: var(--ink); }

/* Searchable variant — search input on top, scrollable list below */
.fr-csel-pop-people { padding: 6px; max-height: 320px; width: 240px; }
.fr-csel-search {
  width: 100%; padding: 6px 8px; border: 1px solid var(--line);
  border-radius: 5px; font-family: inherit; font-size: 12px;
  margin-bottom: 6px; box-sizing: border-box;
}
.fr-csel-search:focus { outline: none; border-color: var(--primary); }
.fr-csel-list { max-height: 240px; overflow-y: auto; }
.fr-csel-opt-check {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 8px; border-radius: 4px; cursor: pointer;
  font-size: 12px; background: var(--card); color: var(--ink);
  margin-bottom: 2px;
}
.fr-csel-opt-check:hover { background: var(--surface); filter: none; }
.fr-csel-opt-check.on { background: #eef5ff; }
.fr-csel-opt-check input[type="checkbox"] {
  accent-color: var(--primary); width: 14px; height: 14px; cursor: pointer; margin: 0;
}
.fr-csel-empty {
  text-align: center; color: var(--ink-soft); font-size: 12px; padding: 12px;
}

.filter-actions { display: flex; gap: 8px; align-items: center; margin-left: auto; }
/* both buttons share the .btn-sm sizing so they're visually equal */
.filter-search-btn, .filter-clear-btn { padding: 6px 14px; font-size: 12px; }
.filter-clear-btn { color: var(--ink-soft); }
.filter-clear-btn:hover { color: var(--red); border-color: #f3c2c8; }

/* Filter config modal — option list of columns to filter on.
   Rendered as a single bordered box with rows inside (no per-row borders). */
.flt-col-list {
  display: flex; flex-direction: column;
  max-height: 320px; overflow-y: auto;
  border: 1px solid var(--line); border-radius: 6px;
  background: var(--card); padding: 4px;
}
/* `.modal label` adds margin-bottom: 14px to every <label> — neutralise that
   in our list rows since they should sit tight to each other. */
.modal label.flt-col-opt,
.flt-col-opt {
  display: flex; align-items: center; gap: 8px; padding: 3px 10px;
  border-radius: 4px; cursor: pointer; font-size: 12.5px; line-height: 1.2;
  background: transparent; margin: 0; font-weight: 400;
}
.flt-col-opt:hover { background: var(--surface); }
.flt-col-opt.on { background: #eef5ff; color: var(--primary); }
.flt-col-opt input[type="checkbox"] { accent-color: var(--primary); width: 14px; height: 14px; cursor: pointer; margin: 0; }
.flt-col-opt .opt-ico { color: var(--ink-soft); display: inline-flex; }
.flt-col-opt .flt-col-type { color: var(--ink-soft); font-size: 11px; margin-left: auto; }
.flt-multi { display: flex; flex-direction: column; gap: 4px; max-height: 240px; overflow-y: auto; padding: 6px; border: 1px solid var(--line); border-radius: 6px; }
.flt-multi-opt { display: flex; align-items: center; gap: 8px; padding: 4px 6px; border-radius: 4px; cursor: pointer; font-size: 13px; }
.flt-multi-opt:hover { background: var(--surface); }
.flt-multi-opt input[type="checkbox"] { accent-color: var(--primary); }
.flt-multi-opt .flt-chip { padding: 2px 8px; border-radius: 10px; color: #fff; font-size: 11px; font-weight: 600; }
.flt-range { display: flex; gap: 8px; align-items: center; }
.flt-range input { flex: 1; padding: 7px 10px; border: 1px solid var(--line); border-radius: 6px; font-family: inherit; font-size: 13px; }
.flt-range input:focus { outline: none; border-color: var(--primary); }
.flt-range-sep { color: var(--ink-soft); }
.board-body { padding: 4px 32px 60px; }

/* ---------- Table ---------- */
.group { margin-bottom: 26px; }
/* Scroll the table itself when columns push it past the viewport, instead
   of letting it overflow into the page's own horizontal scroll. Scrollbar
   hidden — drag-to-scroll wiring lives in app.js. padding-left gives the
   row-menu (positioned at left:-22 relative to the first cell) room inside
   the scroll container so it isn't clipped. */
.group-table {
  overflow-x: auto;
  scrollbar-width: none; -ms-overflow-style: none;
  padding-left: 24px;
}
.group-table::-webkit-scrollbar { display: none; }
/* While the user is actively click-drag-panning the row, suppress text
   selection + show a grabbing cursor so the gesture feels responsive. */
.group-table.dragging,
.group-table.dragging * { user-select: none; cursor: grabbing !important; }

/* Floating horizontal scrollbar — a slim bar pinned to the bottom of the
   viewport (positioned + sized by app.js) that drives the current wide view
   (table / kanban) so its scrollbar is always reachable without scrolling the
   page down. Hidden on touch screens, where swipe/drag-pan already works. */
.float-hscroll {
  position: fixed; bottom: 0; z-index: 25; height: 14px;
  overflow-x: auto; overflow-y: hidden;
  background: var(--card); border-top: 1px solid var(--line);
  display: none;          /* app.js shows it only when the view overflows */
}
.float-hscroll-inner { height: 1px; }
.float-hscroll::-webkit-scrollbar { height: 11px; }
.float-hscroll::-webkit-scrollbar-track { background: transparent; }
.float-hscroll::-webkit-scrollbar-thumb { background: rgba(120,130,145,.5); border-radius: 6px; }
.float-hscroll::-webkit-scrollbar-thumb:hover { background: rgba(120,130,145,.78); }
/* The kanban hides its own bar now that the floating one drives it. */
.kanban { scrollbar-width: none; -ms-overflow-style: none; }
.kanban::-webkit-scrollbar { height: 0; }
@media (max-width: 768px) { .float-hscroll { display: none !important; } }

.group-head {
  display: flex; align-items: center; gap: 8px; margin-bottom: 2px;
  font-weight: 700; font-size: 15px; position: relative;
}
.group-head .g-caret { cursor: pointer; font-size: 12px; }
.group-head .g-title { padding: 2px 6px; border-radius: 5px; border: 1px solid transparent; }
.group-head .g-title:focus { outline: none; }
.group-head .g-count { color: var(--ink-soft); font-weight: 500; font-size: 12px; }
.g-color {
  width: 14px; height: 14px; border-radius: 4px; cursor: pointer;
  display: inline-block; flex-shrink: 0;
}
.g-color:hover { outline: 2px solid var(--line-dark); outline-offset: 1px; }
.color-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.color-dot { width: 26px; height: 26px; border-radius: 6px; cursor: pointer; transition: transform .1s; }
.color-dot:hover { transform: scale(1.15); }
.lbl-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; max-height: 320px; overflow-y: auto; }
.lbl-row { display: flex; align-items: center; gap: 8px; }
.lbl-color {
  width: 26px; height: 26px; border-radius: 6px; cursor: pointer; flex-shrink: 0;
  border: 1px solid rgba(0,0,0,.08);
}
.lbl-color:hover { outline: 2px solid var(--line-dark); outline-offset: 1px; }
.lbl-text {
  flex: 1; padding: 7px 10px; border: 1px solid var(--line-dark);
  border-radius: 6px; font-size: 13px; font-family: inherit;
}
.lbl-del {
  border: none; background: transparent; color: var(--red);
  font-size: 18px; line-height: 1; width: 26px; height: 26px; border-radius: 6px; flex-shrink: 0;
}
.lbl-del:hover { background: #fdeaec; }

.tbl { border-collapse: separate; border-spacing: 0; width: max-content; min-width: 100%; }
.tbl th, .tbl td {
  border-bottom: 1px solid var(--line); border-right: 1px solid var(--line);
  height: 36px; font-size: 13px;
}
.tbl thead th {
  background: var(--card); color: var(--ink-soft); font-weight: 600; font-size: 12px;
  padding: 0 10px; text-align: left; white-space: nowrap; position: relative;
}
.tbl thead th[data-col] { padding-right: 28px; }
.tbl thead th .col-menu {
  position: absolute; right: 6px; top: 0; bottom: 0; margin: 0;
  opacity: 0; cursor: pointer; color: var(--ink-soft); align-items: center;
}
.tbl thead th:hover .col-menu { opacity: 1; }
.g-bar { width: 6px; padding: 0 !important; border-right: none !important; position: relative; }
.tbl tbody tr:hover td { background: #f5f6f8; }
.cell { padding: 0 10px; cursor: pointer; max-width: 280px; }
.cell.cell-name { min-width: 280px; font-weight: 500; cursor: text; }
.cell.cell-name .item-name { display: flex; align-items: center; gap: 8px; }
.item-name-text:focus { outline: none; }
.cell.cell-name .expand { color: var(--ink-soft); font-size: 11px; }
.cell-type-status, .cell-type-dropdown { padding: 0 !important; text-align: center; }
.cell-empty-hint { color: #c3c6d4; }

.add-row td {
  color: var(--ink-soft); font-style: normal; cursor: text;
  background: var(--card);
}
.add-row:hover td { background: #f5f6f8; }
.add-row input {
  border: none; background: transparent; font-size: 13px; width: 100%; padding: 0;
}
.add-row input:focus { outline: none; }

/* ── group summary row ───────────────────────────────────────────── */
.summary-row td {
  background: #fafbfc;
  border-top: 1px solid var(--line);
  font-size: 12px; color: var(--ink); cursor: default;
  padding: 6px 10px;
}
.summary-row:hover td { background: #fafbfc; }
.summary-row .sum-name { color: var(--ink-soft); font-weight: 600; }
.summary-row .sum-count { font-variant-numeric: tabular-nums; }
.summary-row .cell-sum { font-variant-numeric: tabular-nums; }
.summary-row .sum-num,
.ov-sum .sum-num { display: inline-flex; align-items: center; gap: 4px; font-weight: 600; }
.summary-row .sum-lbl,
.ov-sum .sum-lbl { color: var(--ink-soft); font-weight: 700; font-size: 11px; }
.summary-row .sum-bar,
.ov-sum .sum-bar {
  display: inline-flex; width: 100%; height: 16px; border-radius: 4px; overflow: hidden;
  background: #eef0f3; max-width: 220px; min-width: 80px;
}
.summary-row .sum-bar-seg,
.ov-sum .sum-bar-seg { display: block; height: 100%; }


.col-add-th { width: 44px; text-align: center !important; }
.col-add-th button {
  border: none; background: transparent; font-size: 17px; color: var(--ink-soft);
}
.col-add-th button:hover { color: var(--primary); }
.row-menu {
  position: absolute; left: -22px; top: 0; bottom: 0;
  opacity: 0; border: none; background: transparent; color: var(--ink-soft);
  display: flex; align-items: center; justify-content: center; padding: 0;
}
tr:hover .row-menu { opacity: 1; }
.row-menu:hover { color: var(--ink); }

/* ---------- Cell value styles ---------- */
.cv-text { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Join-board: read-only display of values pulled from a matched item on
   another board. Rows stack vertically; each cell flows inline. */
.cv-join { display: flex; flex-direction: column; gap: 2px; }
.cv-join-row { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.cv-join-cell { font-size: 12px; color: var(--ink); }
.cv-join-empty { font-size: 12px; font-style: italic; }

/* Virtual join columns — show_cols expanded into separate visible columns.
   Read-only (no inline editing) so style them with a faint join indicator. */
.cell-join-virtual { background: #fafbfd; }
.cell-join-virtual .cell-box { cursor: default; }
.cv-join-virtual { font-size: 12.5px; }
.th-join-virtual { background: #fafbfd; }
.th-join-virtual .th-join-src { font-style: italic; }
.cv-num { font-variant-numeric: tabular-nums; }

/* ===== Chats view — chatbox boards only ===================================
   A WhatsApp/Messenger-style list of items: avatar + name + last message +
   unread badge. Each row links to the overview drawer's Followup tab. */
.chats-list { display: flex; flex-direction: column; margin: 12px 0 40px; padding: 0 8px; }
.chats-row {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 12px; border-radius: 12px;
  text-decoration: none; color: inherit; cursor: pointer;
  transition: background .12s;
}
.chats-row + .chats-row { border-top: 1px solid var(--line); border-top-left-radius: 0; border-top-right-radius: 0; }
.chats-row:hover { background: rgba(0,115,234,.06); }
.chats-avatar {
  position: relative; flex: none; width: 48px; height: 48px;
  border-radius: 50%;
  background: linear-gradient(135deg, #579bfc, #0073ea);
  display: inline-flex; align-items: center; justify-content: center;
}
/* Round the image itself instead of clipping the avatar with overflow:hidden,
   so the unread badge can stick out of the corner without being clipped. */
.chats-avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }
.chats-initial {
  width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;
  color: #fff; font-weight: 700; font-size: 18px;
}
.chats-badge {
  position: absolute; top: -2px; right: -2px; min-width: 18px; height: 18px;
  padding: 0 5px; border-radius: 9px; background: #e2445c; color: #fff;
  font-size: 11px; font-weight: 700; display: inline-flex; align-items: center; justify-content: center;
  border: 2px solid var(--card);
}
.chats-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.chats-top { display: flex; align-items: baseline; gap: 8px; }
.chats-name { font-weight: 600; font-size: 14.5px; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chats-when { font-size: 11.5px; color: var(--muted); flex: none; }
.chats-preview { font-size: 13px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chats-row.has-unread .chats-name { font-weight: 700; }
.chats-row.has-unread .chats-preview { color: var(--ink); font-weight: 600; }
.chats-row.has-unread .chats-when { color: var(--primary); font-weight: 700; }
.chats-empty p { margin-bottom: 14px; }
.chats-empty .btn { display: inline-flex; align-items: center; gap: 6px; }
/* "เพิ่มห้องแชท" modal — small, single-input. */
.chats-add-modal { width: 380px; max-width: 92vw; }
.chats-add-modal h3 { margin: 0 0 16px; }
.chats-add-label { display: block; font-size: 13px; font-weight: 600; margin-bottom: 14px; }
.chats-add-label input { margin-top: 6px; width: 100%; }
/* "แก้ไขหน่วย" modal for number columns — single input + hint. */
.unit-edit-modal { width: 340px; max-width: 92vw; }
.unit-edit-modal h3 { margin: 0 0 16px; }
.unit-edit-label { display: block; font-size: 13px; font-weight: 600; margin-bottom: 6px; }
.unit-edit-label input { margin-top: 6px; width: 100%; }
.unit-edit-hint { font-size: 12px; margin: 0 0 14px; }
/* Gmail-integrate modal — enable toggle + per-field column mapping */
.gg-modal { width: 560px; max-width: 94vw; }
.gg-toggle-row { display: flex; align-items: center; gap: 9px; font-size: 13.5px; font-weight: 600; margin: 4px 0 8px; cursor: pointer; }
.gg-toggle-row input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--primary); margin: 0; }
.gg-warn { font-size: 12px; color: #b42318; background: #fdeaec; border-radius: 7px; padding: 8px 10px; margin: 0 0 12px; line-height: 1.5; }
.gg-recip { font-size: 12px; margin: 0 0 12px; }
.gg-map-head { display: grid; grid-template-columns: 110px 1fr 1fr; gap: 8px; font-size: 11px; font-weight: 700; color: var(--ink-soft); text-transform: uppercase; letter-spacing: .03em; padding: 0 0 4px; }
.gg-map-head span:first-child { visibility: hidden; }
.gg-map-row { display: grid; grid-template-columns: 110px 1fr 1fr; gap: 8px; align-items: start; margin-bottom: 8px; }
.gg-map-row-noText { grid-template-columns: 110px 1fr; }
.gg-map-label { font-size: 12.5px; font-weight: 600; padding-top: 7px; }
.gg-modal .gg-text { width: 100%; font-size: 12.5px; }
.gg-modal textarea.gg-text { resize: vertical; }
.gg-modal .gg-mount { min-width: 0; }
/* Multi-select blocks (body / cc / bcc) — a labelled searchable dropdown */
.gg-block { margin: 10px 0; }
.gg-block-label { font-size: 12.5px; font-weight: 600; margin-bottom: 5px; }
.gg-block .gg-text { margin-bottom: 6px; }

/* Document-style body editor: formatting toolbar + free text + {data} chips.
   Single row — scrolls sideways on very narrow screens instead of wrapping. */
.gg-body-tools {
  display: flex; align-items: center; flex-wrap: nowrap; gap: 2px;
  padding: 5px 6px; border: 1px solid var(--line-dark); border-bottom: none;
  border-radius: 6px 6px 0 0; background: var(--surface);
  overflow-x: auto; scrollbar-width: thin;
}
.gg-body-tools > * { flex-shrink: 0; }
.gg-body-tools::-webkit-scrollbar { height: 5px; }
.gg-body-tools::-webkit-scrollbar-thumb { background: var(--line-dark, #c7ccd8); border-radius: 3px; }
.gg-body-tools + .gg-body-editor { border-radius: 0 0 6px 6px; }
.gg-fmt-btn {
  min-width: 26px; height: 26px; padding: 0 6px; border: 1px solid transparent;
  border-radius: 5px; background: transparent; color: var(--ink); cursor: pointer;
  font-size: 13px; line-height: 1; display: inline-flex; align-items: center; justify-content: center;
}
.gg-fmt-btn:hover { background: var(--hover, #eef1f6); border-color: var(--line); }
.gg-fmt-btn b { font-weight: 800; } .gg-fmt-i i { font-style: italic; } .gg-fmt-u u { text-decoration: underline; }
.gg-fmt-sep { width: 1px; height: 16px; margin: 0 3px; background: var(--line); }
/* Extra selector weight beats `.modal select { width:100% }` so it stays compact. */
.gg-body-tools .gg-fsize { flex: 0 0 auto; width: auto; min-width: 0; margin: 0; height: 26px; border: 1px solid var(--line); border-radius: 5px; background: var(--card); color: var(--ink); font-size: 12px; font-family: inherit; padding: 0 4px; cursor: pointer; }
/* Colour inputs are round swatches. Extra selector weight beats `.modal input { width:100% }`. */
.gg-body-tools .gg-fcolor { flex: 0 0 auto; width: 22px; height: 22px; min-width: 0; margin: 0; padding: 1px; border: 1px solid var(--line); border-radius: 50%; background: var(--card); cursor: pointer; }
.gg-fcolor::-webkit-color-swatch-wrapper, .gg-bgcolor::-webkit-color-swatch-wrapper { padding: 0; }
.gg-fcolor::-webkit-color-swatch, .gg-bgcolor::-webkit-color-swatch { border: 1px solid var(--line); border-radius: 50%; }
.gg-fcolor::-moz-color-swatch, .gg-bgcolor::-moz-color-swatch { border: 1px solid var(--line); border-radius: 50%; }
.gg-bg-ctl { display: inline-flex; align-items: center; gap: 3px; height: 26px; padding: 0 3px; border: 1px solid var(--line); border-radius: 13px; background: var(--card); color: var(--ink-soft); }
.gg-bg-apply { min-width: 0; width: 20px; height: 20px; padding: 0; border: none; background: none; border-radius: 5px; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; color: var(--ink-soft); }
.gg-bg-apply:hover { background: var(--hover, #eef1f6); }
.gg-bg-apply svg { width: 14px; height: 14px; }
.gg-bg-ctl .gg-bgcolor { flex: 0 0 auto; width: 18px; height: 18px; min-width: 0; margin: 0; padding: 0; border: none; border-radius: 50%; background: none; cursor: pointer; }
.gg-bg-div { width: 1px; height: 16px; background: var(--line); flex-shrink: 0; }
/* Insert-data is icon-only (matches the format buttons). */
.gg-insert-btn { color: var(--primary); }
.gg-insert-btn svg { width: 15px; height: 15px; }
.gg-body-editor {
  min-height: 92px; max-height: 240px; overflow-y: auto;
  border: 1px solid var(--line-dark); border-radius: 6px;
  padding: 8px 10px; font-size: 13px; line-height: 1.6;
  background: var(--card); color: var(--ink); outline: none;
  white-space: pre-wrap; word-break: break-word;
}
.gg-body-editor:focus { border-color: var(--primary); box-shadow: 0 0 0 2px rgba(0,115,234,.15); }
.gg-body-editor:empty::before { content: attr(data-ph); color: var(--ink-soft); }
/* The inserted data "box" — atomic (one backspace removes it whole). */
.gg-token {
  display: inline-block; vertical-align: baseline;
  background: #e7f1ff; color: var(--primary); font-weight: 600;
  border-radius: 5px; padding: 1px 7px; margin: 0 1px; font-size: 12px;
  white-space: nowrap; user-select: none;
}
/* Insert-data picker popover (reuses .popover chrome). */
.gg-pick { min-width: 200px; max-height: 300px; overflow: hidden; display: flex; flex-direction: column; }
.gg-pick .gg-pick-q { margin: 2px 2px 6px; }
.gg-pick-list { overflow-y: auto; }
.gg-pick-opt {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 7px 9px; border: none; background: var(--card); color: var(--ink);
  border-radius: 6px; cursor: pointer; font-size: 13px; text-align: left;
}
.gg-pick-opt:hover { background: var(--surface); }
.gg-pick-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Searchable select dropdown (.ss-dd) — button + popover with search box */
.ss-dd { position: relative; }
.ss-dd-btn {
  display: flex; align-items: center; gap: 6px; width: 100%;
  padding: 6px 9px; font-size: 12.5px; text-align: left;
  border: 1px solid var(--line-dark); border-radius: 6px;
  background: var(--card); color: var(--ink); cursor: pointer;
}
.ss-dd-btn:hover { border-color: var(--primary); }
.ss-dd-lbl { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ss-dd-car { color: var(--ink-soft); font-size: 10px; flex-shrink: 0; }
/* position + coords are set inline (fixed) so the modal's overflow can't clip it */
.ss-dd-pop {
  z-index: 400;
  background: var(--card); border: 1px solid var(--line-dark); border-radius: 8px;
  box-shadow: 0 12px 32px rgba(0,0,0,.18); padding: 5px;
}
.ss-dd-search {
  width: 100%; box-sizing: border-box; margin-bottom: 4px;
  padding: 5px 8px; font-size: 12.5px;
  border: 1px solid var(--line-dark); border-radius: 5px;
  background: var(--card); color: var(--ink);
}
.ss-dd-search:focus { outline: none; border-color: var(--primary); }
.ss-dd-list { max-height: 200px; overflow-y: auto; }
.ss-dd-opt {
  display: flex; align-items: center; gap: 7px;
  padding: 5px 8px; border-radius: 5px; cursor: pointer; font-size: 12.5px;
}
.ss-dd-opt:hover { background: var(--surface); }
.ss-dd-opt.on { background: var(--surface); font-weight: 600; }
.ss-dd-opt input[type="checkbox"] { width: 14px; height: 14px; accent-color: var(--primary); margin: 0; flex-shrink: 0; }
.ss-dd-empty { padding: 6px 8px; font-size: 12px; color: var(--ink-soft); }
/* "ตั้งค่าค่าเริ่มต้น" modal — chip lists for dropdown/people, single-input
   layouts for text/number/etc. Re-uses .unit-edit-modal sizing. */
.dv-body { margin-bottom: 8px; }
.dv-body input.cs-select, .dv-body textarea.cs-select, .dv-body select.cs-select {
  width: 100%;
}
.dv-chip-list { display: flex; flex-wrap: wrap; gap: 6px; max-height: 220px; overflow-y: auto; }
.dv-chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 8px; border: 1px solid #ddd; border-radius: 12px;
  font-size: 12.5px; cursor: pointer; user-select: none;
}
.dv-chip input { margin: 0; }
.dv-cb-row { display: flex; align-items: center; gap: 8px; font-size: 13px; }
.dv-today { display: flex; align-items: center; gap: 6px; font-size: 12.5px; margin-top: 8px; }
.cv-status {
  display: flex; align-items: center; justify-content: center; height: 35px;
  color: #fff; font-weight: 500; font-size: 12.5px; padding: 0 6px;
}
.cv-empty-status { background: #c4c4c4 !important; }
.cv-tag {
  display: inline-block; color: #fff; font-size: 11px; font-weight: 600;
  padding: 2px 8px; border-radius: 11px; margin: 2px;
}
.cv-date { color: var(--ink); }
.cv-link { color: var(--primary); text-decoration: underline; }
/* In the table view, clip long link / email values to the column so they don't
   bleed over the next cell. The full value stays in the href + title tooltip. */
.tbl td.cell .cv-link {
  display: inline-block; max-width: 260px; vertical-align: bottom;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Signature cell — the drawn image, sized to sit inside a table row. */
.cv-sign { display: inline-block; max-height: 30px; max-width: 180px; vertical-align: middle; background: #fff; border-radius: 3px; }
/* Signature drawing modal. */
.sign-modal { width: min(640px, 94vw); }
.sign-pad-wrap { margin: 4px 0 6px; }
.sign-pad {
  display: block; width: 100%; height: auto; aspect-ratio: 600 / 220;
  background: #fff; border: 1px dashed var(--line-dark, #c9cdda); border-radius: 8px;
  cursor: crosshair; touch-action: none;
}
.sign-hint { font-size: 12px; margin: 0 0 6px; }
/* Form-view signature field — preview image + "เซ็นลายเซ็น" button. */
.ff-sign { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ff-sign-prev { max-height: 60px; max-width: 220px; background: #fff; border: 1px solid var(--line); border-radius: 6px; }
.cv-people { display: flex; gap: 3px; }
/* Read-only "เพิ่มโดย" (added_by) cell: creator avatar + name, muted. */
.cv-addedby { display: inline-flex; align-items: center; gap: 6px; }
.cv-addedby-name { font-size: 12.5px; color: var(--ink-soft); white-space: nowrap; }
.cv-avatar {
  width: 26px; height: 26px; border-radius: 50%; background: var(--primary);
  color: #fff; font-size: 11px; font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
}
/* Profile-photo variant of the avatar circle (people columns, everywhere):
   fill the circle, no initials background showing through. */
.cv-avatar-img { object-fit: cover; background: transparent; }
.cv-check {
  width: 20px; height: 20px; border: 2px solid var(--line-dark); border-radius: 5px;
  display: inline-flex; align-items: center; justify-content: center; color: #fff;
}
.cv-check.on { background: var(--green); border-color: var(--green); }
.cv-rating { color: #d0d4e4; letter-spacing: 1px; }
.cv-star.on { color: #fdab3d; }
.cv-chip {
  display: inline-block; background: #e7f1ff; color: var(--primary);
  border-radius: 11px; padding: 2px 9px; font-size: 12px; font-weight: 600; margin: 2px;
}
/* Overflow indicator for board_relation cells holding >4 linked items.
   Muted so it doesn't look like a real linked item; hovering shows the
   exact remaining count via the title attribute. */
.cv-chip.cv-chip-more {
  background: var(--surface); color: var(--ink-soft);
  border: 1px dashed var(--line-dark);
}

/* ---------- Inline editors ---------- */
.cell-edit {
  width: 100%; border: none; outline: none; border-radius: 4px;
  padding: 4px 6px; font-size: 13px; background: var(--card);
}
.popover {
  position: absolute; z-index: 300; background: var(--card); border: 1px solid var(--line-dark);
  border-radius: 8px; box-shadow: 0 12px 32px rgba(0,0,0,.18); padding: 4px; min-width: 180px;
  /* Never wider than the screen — the ⋯ menu sits near the right edge on mobile
     and would otherwise spill past the viewport (JS also clamps the left). */
  max-width: calc(100vw - 16px);
}
.popover .opt {
  padding: 5px 8px; border-radius: 5px; cursor: pointer; color: #fff;
  font-weight: 500; margin-bottom: 1px; font-size: 12.5px;
  display: flex; align-items: center;
}
.popover .opt:last-child { margin-bottom: 0; }
/* Cell-editor popover: search box + scroll container shown when a column has
   many options (status / dropdown / select / people). The options live in
   .pop-opt-scroll so the list scrolls internally instead of running off-screen,
   while the search box + edit/clear footer stay put. */
.popover .pop-search {
  width: 100%; box-sizing: border-box; margin-bottom: 4px;
  padding: 5px 8px; font-size: 12.5px;
  border: 1px solid var(--line-dark); border-radius: 5px;
  background: var(--card); color: var(--ink);
}
.popover .pop-search:focus { outline: none; border-color: var(--primary); }
.popover .pop-opt-scroll { max-height: 240px; overflow-y: auto; }
.popover .opt.plain { color: var(--ink); background: var(--card); }
.popover .opt.plain:hover { background: var(--surface); }
.popover .opt-clear { color: var(--ink-soft); background: var(--card); justify-content: center; }
.opt-ico { display: inline-flex; margin-right: 7px; flex-shrink: 0; }
/* Board-grouped list inside the board_relation picker popover. The
   board name is clickable: tapping it selects (or clears) every item
   that belongs to that board in one go. */
.opt-head.rel-board-head {
  font-size: 11px; font-weight: 700; color: var(--ink-soft);
  text-transform: uppercase; letter-spacing: .04em;
  padding: 6px 8px 4px;
  cursor: pointer; user-select: none;
  border-radius: 4px;
}
.opt-head.rel-board-head:hover { background: var(--surface); color: var(--ink); }
.opt-head.rel-board-head.rel-board-head-all { color: var(--primary); }
.opt-head.rel-board-head:not(:first-child) {
  margin-top: 4px; border-top: 1px solid var(--line); padding-top: 8px;
}
.opt.rel-item-indent {
  padding: 3px 8px 3px 18px;     /* tighter vertical than the default .opt */
  margin-bottom: 0;
  line-height: 1.25;
}

/* ---------- Item overview drawer ----------
   Slides in from the right when a row's overview icon is clicked. The
   backdrop dims the rest; clicking it or the × closes. Body content is
   injected by app.js. */
.ov-backdrop {
  position: fixed; inset: 0; background: rgba(0,0,0,.35); z-index: 210;
  opacity: 0; transition: opacity .2s ease;
}
.ov-backdrop.ov-show { opacity: 1; }
.ov-drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 220;
  width: 65vw; max-width: 65vw;
  background: var(--card); color: var(--ink);
  border-left: 1px solid var(--line);
  box-shadow: -8px 0 30px rgba(0,0,0,.18);
  transform: translateX(100%); transition: transform .24s cubic-bezier(.2,.7,.3,1);
  display: flex; flex-direction: column;
  /* Per-item group colour — set inline by JS; falls back to the brand colour.*/
  --ov-accent: var(--primary);
}
/* Mobile — drawer takes the whole screen so the overview is readable. */
@media (max-width: 768px) {
  .ov-drawer { width: 100vw; max-width: 100vw; border-left: none; }
}
.ov-drawer.ov-open { transform: translateX(0); }
.ov-head {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 18px; flex-shrink: 0;
  /* Group-colour accent stripe at the top of the header. */
  border-top: 3px solid var(--ov-accent);
  border-bottom: 1px solid var(--line);
}
.ov-title { font-size: 16px; font-weight: 700; flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ov-close {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 6px; flex-shrink: 0;
  background: transparent; border: none; color: var(--ink-soft); cursor: pointer;
}
.ov-close:hover { background: var(--surface); color: var(--ink); }
.ov-body { flex: 1; overflow-y: auto; padding: 0; }

/* Drawer tab bar — mirrors the board's .view-tabs look. */
.ov-tabs {
  display: flex; gap: 2px; padding: 0 14px;
  border-bottom: 1px solid var(--line);
  overflow-x: auto; position: sticky; top: 0; z-index: 2; background: var(--card);
}
.ov-tabs::-webkit-scrollbar { display: none; }
.ov-tab {
  flex-shrink: 0; border: none; background: transparent;
  padding: 10px 14px; font-weight: 600; font-size: 13px; color: var(--ink-soft);
  border-bottom: 3px solid transparent; cursor: pointer; white-space: nowrap;
}
.ov-tab:hover { color: var(--ink); }
.ov-tab.active { color: var(--ov-accent); border-bottom-color: var(--ov-accent); }
/* Unread-message count on the Followup tab. */
.ov-tab-badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 17px; height: 17px; padding: 0 5px; margin-left: 5px;
  border-radius: 9px; background: var(--red); color: #fff;
  font-size: 10.5px; font-weight: 700; vertical-align: middle;
}
.ov-panels { padding: 16px 18px; min-width: 0; }
.ov-panel { display: none; min-width: 0; }
.ov-panel.active { display: block; }
.ov-empty { padding: 24px; text-align: center; }

/* Overview form — label column + editable cell column. Cells reuse the
   table view's td.cell so the global cell-edit handlers just work. */
/* table-layout:fixed + the explicit column widths keep the form inside
   the drawer no matter how wide a value is — content wraps instead of
   pushing the table past the panel edge. */
.ov-form { width: 100%; max-width: 100%; border-collapse: collapse; table-layout: fixed; }
.ov-form .ov-frow { border-bottom: 1px solid var(--line); }
.ov-form .ov-flabel {
  width: 36%; padding: 9px 10px 9px 0; vertical-align: top;
  font-size: 12.5px; font-weight: 600; color: var(--ink-soft);
  word-break: break-word;
}
.ov-form .ov-ftype { display: inline-flex; margin-right: 6px; vertical-align: middle; color: var(--ink-soft); }
.ov-form .cell {
  padding: 6px 4px; cursor: pointer; max-width: none;
  word-break: break-word; overflow-wrap: anywhere; white-space: normal;
}
.ov-form .cell .cell-box { white-space: normal; overflow: visible; }
.ov-form .cell .cv-text { white-space: normal; word-break: break-word; }
.ov-noedit .ov-form .cell { pointer-events: none; cursor: default; }

/* Wide tables (task + linked boards) scroll horizontally INSIDE the
   drawer instead of pushing content past the screen edge. The scrollbar
   itself is hidden — users still drag/swipe/wheel to pan. */
.ov-tbl-wrap {
  width: 100%; max-width: 100%; overflow-x: auto;
  scrollbar-width: none;            /* Firefox */
  -ms-overflow-style: none;          /* old Edge / IE */
}
.ov-tbl-wrap::-webkit-scrollbar { display: none; }   /* Chrome / Safari */
/* Generic table for the task + linked-board tabs. */
.ov-tbl { width: max-content; min-width: 100%; border-collapse: collapse; font-size: 12.5px; }
.ov-tbl th {
  text-align: left; font-size: 11px; color: var(--ink-soft); font-weight: 700;
  text-transform: uppercase; letter-spacing: .03em;
  padding: 6px 8px; border-bottom: 1px solid var(--line); white-space: nowrap;
}
.ov-tbl td { padding: 6px 8px; border-bottom: 1px solid var(--line); vertical-align: top; }
.ov-tbl .ov-tname { font-weight: 600; color: var(--ink); }
/* Summary row at the bottom — per-column aggregates like the table view. */
.ov-tbl .ov-sum td {
  border-top: 2px solid var(--line-dark); border-bottom: none;
  background: var(--surface); font-weight: 600; font-size: 12px;
  color: var(--ink-soft);
}
.ov-tbl .ov-sum .ov-tname { color: var(--ink); }

/* Activity search box — filters the timeline below it. The magnifier
   icon sits inside the field on the left; the input pads left to clear
   it. */
.ov-act-search-wrap { position: relative; margin-bottom: 12px; }
.ov-act-search-wrap .icon {
  position: absolute; left: 10px; top: 50%; transform: translateY(-50%);
  color: var(--ink-soft); pointer-events: none;
}
.ov-act-search {
  width: 100%; box-sizing: border-box;
  padding: 8px 10px 8px 32px;
  border: 1px solid var(--line); border-radius: 7px;
  background: var(--card); color: var(--ink); font-family: inherit; font-size: 13px;
}
.ov-act-search:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 2px rgba(0,115,234,.18); }
.ov-act-nomatch { display: none; padding: 18px; text-align: center; }
/* Activity timeline — dot + line per entry. */
.ov-timeline { list-style: none; margin: 0; padding: 4px 0 0; }
.ov-tl-item { display: flex; gap: 10px; padding: 0 0 14px; position: relative; }
.ov-tl-item:not(:last-child)::before {
  content: ""; position: absolute; left: 4px; top: 14px; bottom: -2px;
  width: 2px; background: var(--line);
}
.ov-tl-dot {
  width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; margin-top: 5px;
  background: var(--primary); position: relative; z-index: 1;
}
/* Action-coloured dots: red = destructive, green = additive, blue =
   edits, grey = neutral. */
.ov-tl-dot-red   { background: var(--red); }
.ov-tl-dot-green { background: var(--green); }
.ov-tl-dot-blue  { background: var(--primary); }
.ov-tl-dot-grey  { background: var(--ink-soft); }
.ov-tl-body { flex: 1; min-width: 0; }
.ov-tl-line { font-size: 13px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
/* Small avatar in front of the actor name. */
.ov-tl-av { width: 20px !important; height: 20px !important; font-size: 10px !important; flex-shrink: 0; }
.ov-tl-ent { color: var(--primary); font-weight: 600; }
.ov-tl-det { font-size: 11.5px; margin-top: 1px; }
.ov-tl-when { font-size: 11px; margin-top: 1px; }
/* In-place restore button on a soft-delete Activity entry. */
.ov-tl-acts { margin-top: 5px; }
.ov-tl-restore {
  display: inline-flex; align-items: center; gap: 4px; cursor: pointer;
  font-size: 11.5px; font-weight: 600; color: var(--primary);
  background: var(--surface); border: 1px solid var(--line); border-radius: 6px; padding: 3px 9px;
}
.ov-tl-restore:hover { border-color: var(--primary); background: var(--card); }
.ov-tl-restore:disabled { opacity: .5; cursor: default; }
.ov-tl-restore .icon { width: 13px; height: 13px; }

/* ---- Followup chat ---- */
.fu-wrap { display: flex; flex-direction: column; height: calc(100vh - 170px); height: calc(100dvh - 170px); min-height: 320px; }
.fu-msgs { flex: 1; overflow-y: auto; padding: 4px 2px 12px; display: flex; flex-direction: column; gap: 12px; }
.fu-msg { display: flex; gap: 8px; align-items: flex-start; }
.fu-msg.fu-mine { flex-direction: row-reverse; }
.fu-av { width: 30px !important; height: 30px !important; font-size: 12px !important; flex-shrink: 0; }
.fu-bubble {
  max-width: 78%; background: var(--surface); border: 1px solid var(--line);
  border-radius: 10px; padding: 8px 11px; font-size: 13px;
}
.fu-msg.fu-mine .fu-bubble { background: rgba(0,115,234,.1); border-color: rgba(0,115,234,.25); }
.fu-meta { font-size: 11px; margin-bottom: 3px; }
.fu-meta b { font-size: 12px; }
.fu-text { white-space: pre-wrap; word-break: break-word; line-height: 1.45; }
.fu-mention-tag { color: var(--primary); font-weight: 600; }
.fu-att-img img { max-width: 220px; max-height: 220px; border-radius: 8px; margin-top: 6px; display: block; }
.fu-att-vid { max-width: 240px; max-height: 240px; border-radius: 8px; margin-top: 6px; display: block; }
.fu-att-file {
  display: inline-flex; align-items: center; gap: 5px; margin-top: 6px;
  color: var(--primary); font-weight: 600; word-break: break-all;
}
.fu-att-file .icon { flex-shrink: 0; }
.fu-att-chip .icon { flex-shrink: 0; }

/* Per-message read receipt — sits at the bottom of each bubble. Click to
   expand the reader names. */
.fu-msg-rc { margin-top: 5px; font-size: 10.5px; }
.fu-msg-rc[data-has="1"] .fu-msg-rc-lbl { cursor: pointer; }
.fu-msg-rc-lbl .fu-rc-read   { color: var(--green); font-weight: 600; }
.fu-msg-rc-lbl .fu-rc-unread { color: var(--ink-soft); }
.fu-msg-rc[data-has="1"] .fu-msg-rc-lbl:hover { text-decoration: underline; }
.fu-msg-rc-names {
  margin-top: 3px; padding: 4px 8px; border-radius: 6px;
  background: var(--surface); color: var(--ink-soft); font-size: 11px; line-height: 1.5;
}

/* Composer */
.fu-compose { position: relative; border-top: 1px solid var(--line); padding-top: 10px; }
.fu-row { display: flex; align-items: flex-end; gap: 8px; }
.fu-attach-btn {
  flex-shrink: 0; width: 34px; height: 34px; border-radius: 7px;
  border: 1px solid var(--line); background: var(--card); color: var(--ink-soft);
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
}
.fu-attach-btn:hover { border-color: var(--primary); color: var(--primary); }
.fu-input {
  flex: 1; resize: none; max-height: 120px;
  border: 1px solid var(--line); border-radius: 7px; padding: 8px 10px;
  font-family: inherit; font-size: 13px; background: var(--card); color: var(--ink);
}
.fu-input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 2px rgba(0,115,234,.18); }
.fu-send { flex-shrink: 0; height: 34px; }
.fu-attach-name {
  font-size: 12px; color: var(--ink-soft); margin-bottom: 6px;
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
}
.fu-att-chip {
  display: inline-flex; align-items: center; gap: 4px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 12px; padding: 2px 4px 2px 9px; max-width: 220px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fu-att-x { border: none; background: transparent; color: var(--red); cursor: pointer; font-size: 15px; line-height: 1; padding: 0 2px; }
/* @-mention autocomplete */
.fu-mention-pop {
  position: absolute; bottom: 52px; left: 42px; z-index: 5;
  background: var(--card); border: 1px solid var(--line); border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0,0,0,.16); padding: 4px; min-width: 180px; max-height: 200px; overflow-y: auto;
}
.fu-mention-opt { padding: 7px 10px; border-radius: 5px; font-size: 13px; cursor: pointer; }
.fu-mention-opt:hover { background: var(--surface); }
@media (max-width: 768px) {
  /* Hug the bottom on mobile: the big leftover gap came from the panel's
     16px bottom padding + the full home-indicator safe-area (~34px). Drop the
     panel padding and cap the safe-area clearance to a few px so the composer
     sits just above the screen edge. dvh sizes the chat to the real viewport. */
  .ov-panels { padding-bottom: 0; }
  .fu-wrap { height: calc(100vh - 120px); height: calc(100dvh - 120px); }
  .fu-compose { padding-bottom: min(env(safe-area-inset-bottom, 0px), 8px); }
}
/* The row-overview icon button — sits in the name cell, only shows on
   row hover so it doesn't clutter the table. */
.row-overview {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; flex-shrink: 0;
  margin-left: auto;        /* pin to the right edge of the name cell */
  background: transparent; border: none; border-radius: 5px;
  color: var(--ink-soft); cursor: pointer; opacity: 0;
  transition: opacity .12s, background .12s, color .12s;
}
tr:hover .row-overview { opacity: .7; }
.row-overview:hover { opacity: 1; background: var(--surface); color: var(--primary); }
@media (pointer: coarse) { .row-overview { opacity: .7; } }   /* touch: always visible */
/* Unread-followup badge on a row — always visible (not just on hover) so
   the count is noticeable. */
.row-overview { position: relative; }
.row-fu-badge {
  position: absolute; top: -5px; right: -5px;
  min-width: 15px; height: 15px; padding: 0 4px;
  border-radius: 8px; background: var(--red); color: #fff;
  font-size: 9.5px; font-weight: 700; line-height: 15px; text-align: center;
}
tr .row-overview:has(.row-fu-badge) { opacity: 1; }   /* keep visible when it has a badge */

/* Drag-handle to reorder rows — sits at the left of the name cell. Only this
   grip is draggable, so cell editing / text selection still work. Subtle by
   default, clearer on row/handle hover. Hidden for non-editors. */
.row-drag {
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0; margin-right: 1px;
  color: var(--ink-soft); opacity: .35; cursor: grab;
  transition: opacity .12s, color .12s;
}
tr:hover .row-drag { opacity: .7; }
.row-drag:hover { opacity: 1; color: var(--primary); }
.row-drag:active { cursor: grabbing; }
body.bp-no-edit .row-drag { display: none; }
tr.row-dragging > td { opacity: .4; }
tr.row-dragging .cell-name { background: var(--surface); }

/* Drag-handle to reorder whole groups — sits at the left of the group header. */
.group-drag {
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0; margin-right: 2px;
  color: var(--ink-soft); opacity: .4; cursor: grab;
  transition: opacity .12s, color .12s;
}
.group-head:hover .group-drag { opacity: .8; }
.group-drag:hover { opacity: 1; color: var(--primary); }
.group-drag:active { cursor: grabbing; }
body.bp-no-edit .group-drag { display: none; }
.group.group-dragging { opacity: .55; }

/* Unread-followup badge on a sidebar board link — sits at the far right,
   after the 3-dot menu. When there's no menu, margin-left:auto pushes it
   to the edge; with a menu present, name's flex:1 already does that so
   the auto is a harmless no-op. */
.sb-fu-badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 16px; height: 16px; padding: 0 5px; margin-left: auto; flex-shrink: 0;
  border-radius: 8px; background: var(--red); color: #fff;
  font-size: 10px; font-weight: 700;
}
/* When a badge is present the menu shouldn't also claim the auto margin
   (that would split them apart) — let the badge own the right edge. */
.side-link .sb-menu { margin-left: auto; }
.side-link .sb-menu + .sb-fu-badge { margin-left: 6px; }

/* ---------- Modal ---------- */
/* Smooth open animation: backdrop fades in, dialog scales up + fades.
   Every modal (settings, dialogs, item-detail, share, etc.) gets this
   for free because they all share the .modal-bg + .modal classes. */
@keyframes modalBgIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes modalIn {
  from { opacity: 0; transform: translateY(10px) scale(.96); }
  to   { opacity: 1; transform: translateY(0)    scale(1); }
}
.modal-bg {
  /* Above the overview drawer (z-index 220) so settings modals opened from a
     cell inside the drawer (formula, status/dropdown options, relation/join
     board, confirm/alert/prompt) appear in front of it, not behind. */
  position: fixed; inset: 0; background: rgba(0,0,0,.45); z-index: 230;
  display: flex; align-items: center; justify-content: center;
  animation: modalBgIn .16s ease-out;
}
.modal {
  background: var(--card); border-radius: 12px; padding: 24px; width: 440px;
  box-shadow: 0 30px 60px rgba(0,0,0,.3);
  max-height: 88vh; overflow-y: auto;
  animation: modalIn .22s cubic-bezier(.2,.7,.3,1.2);
  transform-origin: center top;
}
/* Form-view progress popup — spinner while submitting/uploading/emailing,
   then a green tick (auto-closes) or a red cross + close button. */
.form-wait {
  width: auto; min-width: 260px; max-width: 360px; text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  padding: 30px 34px;
}
.fw-icon {
  width: 46px; height: 46px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 26px; font-weight: 700; line-height: 1;
}
.fw-spin {
  border: 4px solid var(--line); border-top-color: var(--primary);
  animation: fwSpin .7s linear infinite;
}
.fw-ok   { background: #e6f8f0; color: #027a48; }
.fw-bad  { background: #fdeaec; color: #b42318; }
.fw-warn { background: #fff4e5; color: #b54708; }
.fw-text { font-size: 14.5px; font-weight: 600; color: var(--ink); }
.fw-sub  { font-size: 12.5px; line-height: 1.5; text-align: center; }
@keyframes fwSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .fw-spin { animation: none; } }

/* Honour reduced-motion preference — skip animation for users that have
   asked for less. */
@media (prefers-reduced-motion: reduce) {
  .modal-bg, .modal { animation: none; }
}
.modal h3 { margin-bottom: 16px; font-size: 17px; }
.modal label { display: block; margin-bottom: 14px; font-weight: 600; font-size: 13px; }
.modal input, .modal select, .modal textarea {
  width: 100%; margin-top: 6px; padding: 7px 10px; font-size: 13px;
  border: 1px solid var(--line-dark); border-radius: 7px; font-weight: 400;
}
.modal .modal-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 16px; }

/* Custom alert/confirm/prompt dialog — replaces browser-native message boxes */
.dialog-modal { width: 380px; padding: 22px 24px; }
.dialog-modal .dialog-title { margin-bottom: 10px; font-size: 15px; font-weight: 700; }
.dialog-modal .dialog-msg { font-size: 14px; color: var(--ink); line-height: 1.5; margin-bottom: 16px; }
.dialog-modal .dialog-input {
  width: 100%; box-sizing: border-box; padding: 9px 11px; font-size: 14px;
  border: 1px solid var(--line-dark); border-radius: 7px;
  font-family: inherit; margin-bottom: 6px;
}
.dialog-modal .dialog-input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px rgba(87,155,252,.18); }
/* Coloured warning blocks used inside dialog messages (e.g. delete-board confirm) */
.dialog-modal .dlg-warn-head { margin-top: 12px; color: #fdab3d; font-weight: 600; }
.dialog-modal .dlg-warn-list { margin: 4px 0 8px 6px; color: var(--primary); }
.dialog-modal .dlg-warn-tail { color: var(--red); font-weight: 600; }
.type-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 6px;
  max-height: 252px; overflow-y: auto; margin: 6px 0 4px; padding-right: 2px;
}
.type-opt {
  display: flex; align-items: center; gap: 9px; padding: 9px 10px;
  border: 1px solid var(--line-dark); border-radius: 8px; cursor: pointer;
  font-size: 13px; font-weight: 500;
}
.type-opt .icon { color: var(--ink-soft); }
.type-opt:hover { background: var(--surface); }
.type-opt.selected { border-color: var(--primary); background: #eaf3ff; }
.type-opt.selected .icon { color: var(--primary); }

/* ---------- Kanban ---------- */
/* No horizontal padding on .kanban itself — its parent .board-body already
   provides 32px L/R, which keeps the first column aligned with the
   .board-actions caption above. */
.kanban { display: flex; gap: 14px; padding: 8px 0 60px; overflow-x: auto; }
.kanban-col {
  width: 280px; flex-shrink: 0; background: var(--surface);
  border-radius: 10px; padding: 10px;
}
.kanban-col h4 {
  font-size: 13px; padding: 6px 8px; border-radius: 6px; margin-bottom: 8px;
  color: #fff; display: flex; justify-content: space-between;
}
.kanban-card {
  background: var(--card); border: 1px solid var(--line); border-radius: 8px;
  margin-bottom: 8px; cursor: grab;
  overflow: hidden; /* clip the coloured side stripe to the rounded corners */
  display: flex; align-items: stretch;
}
.kanban-card:hover { border-color: var(--primary); }
.kanban-card.dragging { opacity: .4; }
.kanban-col.drop-target { outline: 2px dashed var(--primary); }
.kanban-card .kc-bar { width: 4px; flex-shrink: 0; align-self: stretch; }
.kanban-card .kc-body { padding: 8px 12px; display: flex; flex-direction: column; gap: 1px; flex: 1; min-width: 0; }
.kanban-card .kc-title-row {
  display: flex; align-items: center; gap: 6px; min-width: 0;
  margin-bottom: 2px;
}
.kanban-card .kc-field { padding: 1px 0; }
.kanban-card .kc-title {
  font-weight: 600; font-size: 13.5px; color: var(--ink); line-height: 1.3;
  flex: 1; min-width: 0;
  /* Truncate long names to a single line with "..." */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.kanban-card .kc-checks { display: inline-flex; gap: 3px; flex-shrink: 0; }
.kanban-card .kc-check {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border: 1.5px solid var(--line-dark);
  border-radius: 4px; color: transparent; background: var(--card);
}
.kanban-card .kc-check.on { background: var(--green); border-color: var(--green); color: #fff; }
.kanban-card .kc-file-count {
  display: inline-flex; align-items: center; gap: 3px; flex-shrink: 0;
  padding: 2px 6px; border: 1px solid var(--line-dark); border-radius: 10px;
  background: var(--bg); color: var(--ink-soft);
  font-size: 11px; font-weight: 600; line-height: 1;
}
.kanban-card .kc-file-count svg { width: 12px; height: 12px; }
.kanban-card .kc-field {
  display: flex; align-items: center; gap: 6px;
  font-size: 12px; color: var(--ink-soft); min-width: 0;
}
.kanban-card .kc-icon { color: var(--ink-soft); flex-shrink: 0; display: inline-flex; }
.kanban-card .kc-val { flex: 1; min-width: 0; color: var(--ink); overflow: hidden; }
/* Compact inline rendering of cell values inside a kanban card */
.kanban-card .kc-val .cv-status,
.kanban-card .kc-val .cv-tag,
.kanban-card .kc-val .cv-avatar,
.kanban-card .kc-val .cv-chip,
.kanban-card .kc-val .cv-text,
.kanban-card .kc-val .cv-num,
.kanban-card .kc-val .cv-date,
.kanban-card .kc-val .cv-formula,
.kanban-card .kc-val .cv-rating,
.kanban-card .kc-val .cv-link { display: inline-block; vertical-align: middle; }
.kanban-card .kc-val .cv-people { display: inline-flex; flex-wrap: wrap; gap: 2px; }
.kanban-card .kc-val .cv-files { display: inline-flex; flex-wrap: wrap; gap: 4px; }
/* People avatars get their own row at the bottom of each card so they're
   always visible regardless of how many other fields the card has. */
.kanban-card .kc-people-row {
  display: flex; flex-wrap: wrap; gap: 3px;
  padding-top: 4px; border-top: 1px dashed var(--line);
  margin-top: 2px;
}
.kanban-card .kc-people-row .cv-avatar {
  width: 22px; height: 22px; font-size: 10px;
  background: var(--primary); color: #fff;
  border-radius: 50%; display: inline-flex;
  align-items: center; justify-content: center; font-weight: 700;
  border: 1px solid #fff; cursor: default;
}
.kanban-card .kc-sub { color: var(--ink-soft); font-size: 12px; font-weight: 400; margin-top: 4px; }

.empty-state { text-align: center; padding: 70px 20px; color: var(--ink-soft); }
.empty-state .es-icon { color: var(--line-dark); margin-bottom: 6px; display: flex; justify-content: center; }

/* ---------- Icons ---------- */
.icon { display: inline-block; vertical-align: -0.16em; flex-shrink: 0; }
.btn .icon { vertical-align: middle; }
.side-link .ico, .side-section button, .brand .logo,
.b-icon, .bc-icon, .g-caret, .col-menu, .row-menu, .th-ico,
.col-add-th button, .cv-check {
  display: inline-flex; align-items: center; justify-content: center;
}
.brand .logo { color: #6c8cff; }
.b-icon { color: var(--primary); }
.g-caret { transition: transform .15s ease; color: var(--ink-soft); }
.group.collapsed .g-caret { transform: rotate(-90deg); }
.group.collapsed .group-table { display: none; }
.group-divider { display: none; height: 1px; border-radius: 1px; margin-top: 4px; }
.group.collapsed .group-divider { display: block; }
.th-ico { color: var(--ink-soft); vertical-align: middle; }
.th-name {
  padding: 1px 5px; border: 1px solid transparent; border-radius: 4px;
  cursor: text; vertical-align: middle;
}
.th-name:focus { outline: none; background: transparent; }
.view-tabs a { display: inline-flex; align-items: center; gap: 6px; }
.cv-check .icon { stroke-width: 3; }

/* ===================== Board views ===================== */

/* Calendar */
.cal-head { display: flex; align-items: center; justify-content: center; gap: 18px; margin-bottom: 14px; }
.cal-head h3 { font-size: 17px; }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.cal-dow { text-align: center; font-size: 12px; font-weight: 700; color: var(--ink-soft); padding: 4px; }
.cal-cell {
  min-height: 96px; background: var(--card); border: 1px solid var(--line);
  border-radius: 8px; padding: 6px;
}
.cal-cell.cal-empty { background: transparent; border: none; }
.cal-cell.cal-today { border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary) inset; }
.cal-daynum { font-size: 12px; font-weight: 700; color: var(--ink-soft); margin-bottom: 4px; }
.cal-item {
  background: #e7f1ff; color: var(--primary); font-size: 11.5px; font-weight: 600;
  border-radius: 5px; padding: 3px 6px; margin-bottom: 3px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Calendar — modern FullCalendar theme */
.fc-wrap {
  background: var(--card); border: 1px solid var(--line); border-radius: 16px;
  padding: 22px 24px 26px; max-width: 1180px;
  box-shadow: 0 6px 28px rgba(20,30,80,.07);
}
.fc {
  --fc-border-color: #edeff3;
  --fc-page-bg-color: #fff;
  --fc-neutral-bg-color: #f6f7fb;
  --fc-today-bg-color: #eff4ff;
  --fc-highlight-color: #e7f1ff;
  --fc-list-event-hover-bg-color: #f6f7fb;
  --fc-event-border-color: transparent;
  font-family: inherit;
}
.fc .fc-toolbar.fc-header-toolbar { margin-bottom: 20px; gap: 10px; flex-wrap: wrap; }
.fc .fc-toolbar-title { font-size: 21px; font-weight: 800; color: var(--ink); letter-spacing: -.01em; }
.fc .fc-button {
  background: var(--surface); border: 1px solid var(--line); color: var(--ink);
  font-size: 11.5px; font-weight: 600; text-transform: none;
  padding: 4px 9px; border-radius: 7px; box-shadow: none !important;
  transition: background .12s, color .12s;
}
.fc .fc-button:hover { background: #e9ebf2; color: var(--ink); }
.fc .fc-button:focus { outline: none; }
.fc .fc-button-primary:not(:disabled).fc-button-active,
.fc .fc-button-primary:not(:disabled):active {
  background: var(--primary); border-color: var(--primary); color: #fff;
}
.fc .fc-button-primary:disabled { background: var(--surface); border-color: var(--line); color: #b9bdca; opacity: 1; }
.fc .fc-button .fc-icon { font-size: 1.05em; vertical-align: middle; }
.fc .fc-button-group > .fc-button:not(:last-child) { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.fc .fc-button-group > .fc-button:not(:first-child) { border-top-left-radius: 0; border-bottom-left-radius: 0; margin-left: -1px; }
.fc .fc-scrollgrid { border-radius: 12px; overflow: hidden; }
.fc .fc-scrollgrid-section > * { border-color: var(--fc-border-color); }
.fc .fc-col-header-cell { background: var(--surface); }
.fc .fc-col-header-cell-cushion {
  font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; color: var(--ink-soft); padding: 10px 6px;
}
.fc .fc-daygrid-day-number { font-size: 12.5px; font-weight: 600; color: var(--ink-soft); padding: 7px 9px; }
.fc .fc-daygrid-day:hover { background: #fafbfe; }
.fc .fc-day-today .fc-daygrid-day-number {
  color: #fff; background: var(--primary); border-radius: 50%;
  width: 25px; height: 25px; display: flex; align-items: center; justify-content: center;
  margin: 5px; padding: 0;
}
.fc .fc-day-other .fc-daygrid-day-number { color: #c5c8d4; }
.fc .fc-daygrid-event {
  border-radius: 7px; padding: 3px 8px; margin: 2px 5px;
  font-size: 11.5px; font-weight: 600; line-height: 1.4;
}
.fc .fc-daygrid-event:hover { filter: brightness(.94); }
.fc .fc-daygrid-event-dot { display: none; }
.fc .fc-event-time { font-weight: 700; opacity: .85; }
.fc .fc-daygrid-more-link { font-size: 11px; font-weight: 700; color: var(--primary); margin: 2px 6px; }
.fc-event { cursor: default; }
/* ---- Custom event content: title pieces with leading type-icons ---- */
.fc .fc-evt-row {
  display: inline-flex; align-items: center; gap: 4px;
  min-width: 0; max-width: 100%;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.fc .fc-evt-time {
  font-weight: 700; opacity: .9; margin-right: 4px; flex-shrink: 0;
}
.fc .fc-evt-part {
  display: inline-flex; align-items: center; gap: 3px; min-width: 0;
}
.fc .fc-evt-ic {
  display: inline-flex; align-items: center; opacity: .85; flex-shrink: 0;
}
.fc .fc-evt-ic svg { width: 11px; height: 11px; }
/* Source board icon — shown before the detail on the merged home calendar. */
.fc .fc-evt-bico { display: inline-flex; align-items: center; flex-shrink: 0; margin-right: 4px; opacity: .9; }
.fc .fc-evt-bico svg { width: 12px; height: 12px; }
.fc .fc-evt-val { overflow: hidden; text-overflow: ellipsis; }
.fc .fc-evt-sep { opacity: .55; flex-shrink: 0; }
/* List view rows render eventContent too — drop the icon size a touch and
   let the row wrap onto multiple lines for readability. */
.fc .fc-list-event-title .fc-evt-row { white-space: normal; }
.fc .fc-list-event-title .fc-evt-ic svg { width: 12px; height: 12px; }
.fc .fc-list { border-radius: 12px; overflow: hidden; border-color: var(--fc-border-color); }
.fc .fc-list-day-cushion { background: var(--surface); font-weight: 700; }
.fc .fc-list-event td { cursor: default; }
.fc .fc-list-event-dot { border-width: 5px; }
.fc .fc-list-empty { background: var(--card); color: var(--ink-soft); }
.fc .fc-timegrid-slot { height: 38px; }
.fc .fc-timegrid-now-indicator-line { border-color: var(--red); }

/* Gallery */
/* ----- Gallery view — image/file-first cards ----- */
.gallery {
  display: grid; gap: 16px;
  grid-template-columns: repeat(5, minmax(0, 1fr));
}
/* Only collapse on truly small viewports — desktops should always stay 5 wide.
   Mobile keeps 2 cards per row down to the smallest phone width: hero
   thumbnails read fine at half-screen and the 2-up keeps the gallery
   feel rather than degrading to a list. */
@media (max-width: 900px) { .gallery { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 600px) { .gallery { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; } }
.gal-card {
  background: var(--card); border: 1px solid var(--line); border-radius: 12px;
  overflow: hidden; display: flex; flex-direction: column;
  transition: box-shadow .15s, border-color .15s;
}
.gal-card:hover { box-shadow: 0 8px 22px rgba(0,0,0,.08); border-color: var(--line-dark); }

/* Hero — square-ish frame at the top of every card */
.gal-hero {
  position: relative; width: 100%; aspect-ratio: 16 / 10;
  /* Plain neutral background — padding gives the image breathing room on
     every side, matching the .gal-bar accent width. */
  background: var(--bg);
  padding: 4px;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
.gal-hero-img { width: 100%; height: 100%; display: block; border-radius: 6px; overflow: hidden; }
.gal-hero-img img { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: 6px; }

/* Source-column chip — floats top-left over the hero, names the column that
   the currently displayed file came from. */
.gal-hero .gal-src {
  position: absolute; top: 8px; left: 8px; z-index: 2;
  max-width: calc(100% - 56px);
  padding: 3px 8px; border-radius: 10px;
  background: rgba(0, 0, 0, .55); color: #fff;
  font-size: 11px; font-weight: 600; line-height: 1.4;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  opacity: 0; transition: opacity .15s;
  pointer-events: none;  /* purely a label, don't intercept clicks */
}
.gal-card:hover .gal-src { opacity: 1; }

/* Download corner button — floats top-right over the hero. Sits inside the
   inner padded area (hero has padding:4px) so it doesn't bleed into the frame. */
.gal-hero .gal-dl {
  position: absolute; top: 8px; right: 8px; z-index: 2;
  width: 26px; height: 26px; border-radius: 50%;
  background: rgba(0, 0, 0, .55); color: #fff;
  display: inline-flex; align-items: center; justify-content: center;
  text-decoration: none;
  opacity: 0; transition: opacity .15s, background .15s, transform .12s;
}
.gal-card:hover .gal-dl { opacity: 1; }
.gal-hero .gal-dl:hover { background: rgba(0, 0, 0, .8); transform: scale(1.06); }
.gal-hero .gal-dl { border: 0; padding: 0; cursor: pointer; }

/* Popup menu shown when an item has >1 file — "this file" vs "all".
   Appended to <body> with position:fixed (set in JS) so it escapes the
   card's overflow:hidden clip. */
.gal-dl-menu {
  position: fixed; z-index: 1000;
  background: var(--card); border: 1px solid var(--line); border-radius: 8px;
  box-shadow: 0 10px 24px rgba(0, 0, 0, .14);
  padding: 4px; min-width: 220px;
  display: flex; flex-direction: column; gap: 2px;
}
.gal-dl-opt {
  display: block; width: 100%; text-align: left;
  padding: 8px 10px; font-size: 12.5px; color: var(--ink);
  background: transparent; border: 0; border-radius: 5px; cursor: pointer;
}
.gal-dl-opt:hover { background: var(--bg); color: var(--primary); }
.gal-hero-empty {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  color: #b9bccc; font-size: 12px;
}
.gal-hero-empty svg { color: #d6d8e1; }

/* Non-image hero: big icon centred, with extension chip + filename */
.gal-hero-file {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; width: 100%; height: 100%; padding: 12px;
  color: var(--ink-soft); text-decoration: none;
  background: linear-gradient(180deg, #f6f7fb 0%, #eef0f6 100%);
  border-radius: 6px;
}
.gal-hero-file svg { color: var(--ink-soft); }
.gal-hero-ext {
  font-size: 11px; font-weight: 700; letter-spacing: .04em;
  padding: 2px 8px; border-radius: 12px;
  background: rgba(0,0,0,.06); color: var(--ink);
}
.gal-hero-name {
  font-size: 12px; max-width: 90%;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  color: var(--ink-soft); text-align: center;
}
/* Distinct tints per file kind so the user gets a quick read */
.gal-hero-file.gal-kind-pdf   { background: linear-gradient(180deg,#ffeaea,#ffd8d8); }
.gal-hero-file.gal-kind-pdf .gal-hero-ext { background:#ef4444; color:#fff; }
.gal-hero-file.gal-kind-doc   { background: linear-gradient(180deg,#e8f1ff,#d6e6ff); }
.gal-hero-file.gal-kind-doc .gal-hero-ext { background:#2563eb; color:#fff; }
.gal-hero-file.gal-kind-xls   { background: linear-gradient(180deg,#e8f8ee,#cfeedd); }
.gal-hero-file.gal-kind-xls .gal-hero-ext { background:#10b981; color:#fff; }
.gal-hero-file.gal-kind-ppt   { background: linear-gradient(180deg,#ffeede,#ffd9bf); }
.gal-hero-file.gal-kind-ppt .gal-hero-ext { background:#f97316; color:#fff; }
.gal-hero-file.gal-kind-zip   { background: linear-gradient(180deg,#f1e9ff,#e0d2ff); }
.gal-hero-file.gal-kind-zip .gal-hero-ext { background:#8b5cf6; color:#fff; }
.gal-hero-file.gal-kind-audio { background: linear-gradient(180deg,#ffeaf5,#ffd6ec); }
.gal-hero-file.gal-kind-audio .gal-hero-ext { background:#ec4899; color:#fff; }
.gal-hero-file.gal-kind-video { background: linear-gradient(180deg,#e7eaff,#d3d9ff); }
.gal-hero-file.gal-kind-video .gal-hero-ext { background:#6366f1; color:#fff; }

/* Thumbnail strip — scrolls horizontally if too many to fit (scrollbar hidden) */
.gal-thumbs {
  display: flex; gap: 5px; padding: 6px 8px;
  background: #fcfcfd; border-top: 1px solid var(--line);
  overflow-x: auto;
  scrollbar-width: none;          /* Firefox */
  -ms-overflow-style: none;        /* Edge legacy */
}
.gal-thumbs::-webkit-scrollbar { display: none; }   /* Chrome / Safari */
.gal-thumbs { cursor: grab; user-select: none; }
.gal-thumbs.dragging { cursor: grabbing; }
.gal-thumbs img { -webkit-user-drag: none; user-drag: none; }
.gal-thumb {
  flex-shrink: 0; width: 42px; height: 42px;
  border: 2px solid transparent; border-radius: 6px; padding: 0;
  background: var(--bg); cursor: pointer; overflow: hidden;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  font-size: 9px; line-height: 1.1; color: var(--ink-soft); gap: 1px;
  transition: border-color .12s, transform .08s;
}
.gal-thumb:hover { transform: translateY(-1px); }
.gal-thumb.on { border-color: var(--primary); }
.gal-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.gal-thumb .gal-thumb-icon { display: inline-flex; }
.gal-thumb .gal-thumb-ext { font-weight: 700; }
.gal-thumb.gal-kind-pdf   { background:#ffe2e2; color:#b91c1c; }
.gal-thumb.gal-kind-doc   { background:#dfeaff; color:#1d4ed8; }
.gal-thumb.gal-kind-xls   { background:#daf3e4; color:#047857; }
.gal-thumb.gal-kind-ppt   { background:#ffe0c4; color:#c2410c; }
.gal-thumb.gal-kind-zip   { background:#ebdcff; color:#6d28d9; }
.gal-thumb.gal-kind-audio { background:#ffd9eb; color:#be185d; }
.gal-thumb.gal-kind-video { background:#dde1ff; color:#4338ca; }

/* Body — accent stripe + name + a few key fields */
.gal-body { display: flex; padding: 0; }
.gal-bar { width: 4px; flex-shrink: 0; align-self: stretch; }
.gal-info { flex: 1; min-width: 0; padding: 10px 14px 12px; }
.gal-title {
  font-weight: 700; font-size: 14px; color: var(--ink);
  margin-bottom: 6px; line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.gal-field {
  display: flex; align-items: center; gap: 6px;
  font-size: 12px; color: var(--ink); min-width: 0;
  padding: 2px 0;
}
.gal-icon { color: var(--ink-soft); flex-shrink: 0; display: inline-flex; }
.gal-value { flex: 1; min-width: 0; overflow: hidden; }
/* Compact inline values inside the info row, same approach as kanban cards.
   .cv-avatar is intentionally NOT in this list — it must keep its base
   inline-flex so the initials of a photo-less avatar stay centred (inline-block
   would drop the flex centring and shove the initials to the top-left). */
.gal-value .cv-status, .gal-value .cv-tag,
.gal-value .cv-chip, .gal-value .cv-text, .gal-value .cv-num,
.gal-value .cv-date, .gal-value .cv-formula, .gal-value .cv-rating,
.gal-value .cv-link { display: inline-block; vertical-align: middle; }
.gal-value .cv-people { display: inline-flex; flex-wrap: wrap; gap: 2px; }
.gal-value .cv-avatar { vertical-align: middle; }
/* The default .cv-status is a full-row flex pill (35px tall). In a small card
   it looks oversized — squash it to a compact inline chip here. */
.gal-value .cv-status {
  height: auto; padding: 2px 8px; border-radius: 4px;
  font-size: 11px; font-weight: 600; line-height: 1.4;
  display: inline-block; vertical-align: middle;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.gal-label { color: var(--ink-soft); }
.gal-value { text-align: right; }

/* Chart + dashboard bars */
.chart-card { background: var(--card); border: 1px solid var(--line); border-radius: 10px; padding: 20px; max-width: 720px; }
.chart-total { font-size: 14px; margin-bottom: 14px; }
.chart-bars { display: flex; flex-direction: column; gap: 10px; }
.chart-row { display: flex; align-items: center; gap: 10px; }
.chart-name { width: 130px; font-size: 13px; font-weight: 600; flex-shrink: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chart-track { flex: 1; background: var(--surface); border-radius: 6px; height: 22px; }
.chart-fill { height: 100%; border-radius: 6px; min-width: 4px; transition: width .3s; }
.chart-val { width: 90px; font-size: 13px; font-weight: 700; text-align: right; flex-shrink: 0; }
.chart-val small { color: var(--ink-soft); font-weight: 500; }
.chart-foot { margin-top: 12px; font-size: 12px; }

/* Form view — outer 12-column grid (same system the dashboard widgets use).
   The form itself gets a `grid-column: span N` inline style from PHP, where
   N is the user's chosen 1..12 size level. */
.form-grid {
  display: grid; grid-template-columns: repeat(12, 1fr); gap: 16px;
}
.form-view {
  background: var(--card); border: 1px solid var(--line); border-radius: 12px;
  padding: 26px;
  /* Inner 2-column field layout — long_text and the submit row span the whole
     width via the rule a few lines below. */
  display: grid; grid-template-columns: 1fr 1fr; gap: 14px 18px;
  min-width: 0;   /* let the grid track shrink below content min-content */
}
.form-view h2 { font-size: 19px; }
.form-view > h2, .form-view > .form-head, .form-view > p,
.form-view > .form-field[data-type="long_text"],
.form-view > .form-field:last-child {
  grid-column: 1 / -1;
}
.form-view > p { margin: 0; }
.form-field { margin-bottom: 0; min-width: 0; }
@media (max-width: 640px) {
  .form-view { grid-template-columns: 1fr; }
  /* Tight viewports — collapse the outer 12-col grid so the form takes the
     whole width regardless of the saved level. */
  .form-grid > .form-view { grid-column: 1 / -1 !important; }
}
.form-field label {
  display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap;
  font-size: 11.5px; font-weight: 600;
  /* Labels — and the .req asterisk inside them — pick up the form-view's
     --form-text custom property (set by the gear menu's text-colour picker)
     and fall back to --ink when the user hasn't customised anything. */
  color: var(--form-text, var(--ink)); margin-bottom: 4px;
}
/* Inputs match the filter bar's .fr-input look so the visual language is one. */
.ff-input {
  width: 100%; box-sizing: border-box;
  padding: 5px 8px; border: 1px solid var(--line); border-radius: 5px;
  font-family: inherit; font-size: 12px; background: var(--card);
  /* Inputs have their OWN colour token (--form-input-text). When it isn't set
     they fall back to the label colour (--form-text), then the default ink —
     so boards that only set one colour keep their old look. */
  color: var(--form-input-text, var(--form-text, var(--ink)));
}
/* Form-settings: one row per colour picker (label + swatch + reset). */
.ff-color-row { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.ff-color-row:last-child { margin-bottom: 0; }
.ff-color-row .ff-color-lab { min-width: 160px; font-size: 12.5px; }
/* Form-settings: "show column-type icon" toggle. */
/* Scoped under .cs-set-modal so it beats `.cs-set-modal label { margin:0 }`
   (which has higher specificity than a bare .ff-typeicon-opt). */
.cs-set-modal .ff-typeicon-opt { display: flex; align-items: center; gap: 7px; margin-top: 10px; font-size: 12.5px; cursor: pointer; }
.cs-set-modal .ff-typeicon-opt input { width: 15px; height: 15px; accent-color: var(--primary); }
.ff-input:focus {
  outline: none; border-color: var(--primary);
  box-shadow: 0 0 0 2px rgba(87,155,252,.18);
}
.ff-input[type=checkbox] {
  width: 16px; height: 16px; padding: 0; accent-color: var(--primary);
  vertical-align: middle;
}
.ff-input[type=date] { font-family: inherit; }
select.ff-input { cursor: pointer; appearance: auto; }
select.ff-input[multiple] { min-height: 96px; padding: 4px 6px; }
.form-msg { color: var(--green); font-weight: 600; font-size: 13px; }
.req { color: var(--red); }

/* Column-type icon before each field label — shown only when the form's
   data-typeicon flag is on (toggled in form settings). */
.ff-label-ico { display: none; align-items: center; margin-right: 5px; color: var(--ink-soft); }
.ff-label-ico svg { width: 14px; height: 14px; vertical-align: middle; }
.form-view[data-typeicon="1"] .ff-label-ico { display: inline-flex; }

/* ── Form layout 2 — label + control joined in one input-group ──────────────
   The column label becomes a coloured "addon" to the LEFT of the control:
   [ ชื่อรายการ | input ]. Addon text = dark shade of the column-text colour,
   addon background = light tint of the same hue (set inline as CSS vars).
   The submit row (.form-submit-row) is excluded — it keeps its plain button. */
.form-view[data-layout="2"] > .form-field:not(.form-submit-row) {
  display: flex;
  align-items: stretch;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--card);
  /* No overflow:hidden — it would clip the search/select popover menus. */
}
.form-view[data-layout="2"] > .form-field:not(.form-submit-row) > label {
  /* Fixed width (not a %) so the label column is identical on every field —
     a full-width field (e.g. long_text) no longer gets a wider label. */
  flex: 0 0 150px;
  max-width: 45%;
  display: flex;
  align-items: center;
  gap: 5px;
  margin: 0;
  padding: 6px 10px;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.3;
  background: var(--form-label-bg, #eef1f6);
  color: var(--form-label-text, #2a3140);
  border-right: 1px solid var(--line);
  border-radius: 6px 0 0 6px;        /* round left to match the group frame */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* The control side fills the rest of the group. */
.form-view[data-layout="2"] > .form-field:not(.form-submit-row) > :not(label) {
  flex: 1 1 auto;
  min-width: 0;
}
/* Inputs + the search/select widget buttons lose their own frame so the group
   reads as one joined box. */
.form-view[data-layout="2"] > .form-field:not(.form-submit-row) .ff-input,
.form-view[data-layout="2"] > .form-field:not(.form-submit-row) .fr-csel-btn {
  border: none;
  border-radius: 0;
  background: transparent;
  width: 100%;
}
.form-view[data-layout="2"] > .form-field:not(.form-submit-row) .ff-input:focus,
.form-view[data-layout="2"] > .form-field:not(.form-submit-row) .fr-csel-btn:focus {
  box-shadow: none;
  background: var(--hover, rgba(0,0,0,.02));
}
/* Checkbox shouldn't stretch full width — reset the width the flatten rule
   forces to 100%, so it stays a small box flush to the left of its cell. */
.form-view[data-layout="2"] > .form-field:not(.form-submit-row) > .ff-input[type=checkbox] {
  flex: 0 0 auto;
  width: 16px;
  margin: 7px 8px;
}
/* Long-text textarea collapses to a single-row height so its group lines up
   with the other inputs (drag the corner to expand when needed). */
.form-view[data-layout="2"] > .form-field[data-type="long_text"] > textarea.ff-input {
  height: 28px;
  min-height: 28px;
  box-sizing: border-box;
  padding: 5px 8px;
  resize: vertical;
}

/* Form view title row — name on the left, settings cog on the right */
.form-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.form-head h2 { margin: 0; }
.form-settings-btn {
  width: 28px; height: 28px; border: 1px solid var(--line); border-radius: 6px;
  background: var(--card); color: var(--ink-soft); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
.form-settings-btn:hover { color: var(--primary); border-color: var(--primary); }

/* Calendar gear — same look as form-settings-btn but sits inside .board-actions
   in front of the muted caption. */
.cal-settings-btn {
  width: 26px; height: 26px; border: 1px solid var(--line); border-radius: 6px;
  background: var(--card); color: var(--ink-soft); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
.cal-settings-btn:hover { color: var(--primary); border-color: var(--primary); }

/* ----- Settings modals — numbered card blocks (calendar / gantt / join_board) ----- */
.cs-set-modal { width: 520px; max-width: 92vw; }
.jb-set-modal { width: 580px; max-width: 94vw; }
.cs-set-modal h3, .jb-set-modal h3 { margin: 0 0 14px; }
/* `.modal input/select/textarea` forces width:100% + heavy padding which makes
   our small radios and checkboxes render as massive boxes. Reset them here. */
.cs-set-modal input[type="radio"],
.cs-set-modal input[type="checkbox"] {
  width: auto; margin: 0; padding: 0; border: 0; border-radius: 0;
  height: auto;
}
/* Same — labels inside .modal default to display:block + margin-bottom:14px +
   font-weight:600, which breaks our flex/grid card rows. Reset for our blocks. */
.cs-set-modal label { margin: 0; font-weight: 400; font-size: 12.5px; display: flex; }
.cs-set-modal .cs-block {
  background: var(--card); border: 1px solid var(--line); border-radius: 8px;
  padding: 12px 14px; margin-bottom: 12px;
}
.cs-set-modal .cs-block-head {
  display: flex; align-items: flex-start; gap: 10px; margin-bottom: 10px;
}
.cs-set-modal .cs-block-num {
  width: 22px; height: 22px; border-radius: 50%; flex-shrink: 0;
  background: var(--primary); color: #fff;
  font-size: 12px; font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center;
}
.cs-set-modal .cs-block-titles { flex: 1; min-width: 0; }
.cs-set-modal .cs-block-title {
  font-size: 13.5px; font-weight: 600; color: var(--ink); line-height: 1.3;
}
.cs-set-modal .cs-block-sub {
  font-size: 11.5px; color: var(--ink-soft); margin-top: 2px; line-height: 1.4;
}

/* 1. Date column select */
.cs-set-modal .cs-select {
  width: 100%; height: 32px; padding: 0 10px;
  border: 1px solid var(--line); border-radius: 6px;
  background: var(--card); font-size: 13px; color: var(--ink);
}
.cs-set-modal .cs-select:focus { outline: 2px solid var(--primary); outline-offset: -1px; }

/* Paired start/end date selects on one row (Gantt main + background bars). */
.cs-set-modal .cs-daterow { display: flex; gap: 10px; margin-top: 8px; }
.cs-set-modal .cs-daterow-item { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.cs-set-modal .cs-daterow-lbl { font-size: 11.5px; color: var(--ink-soft); font-weight: 600; }
.cs-set-modal .cs-daterow .cs-select { margin-top: 0; }

/* Two-column inner layout — used by join_board modal to pair source/target keys */
.cs-set-modal .cs-row {
  display: grid; grid-template-columns: 1fr auto 1fr;
  gap: 10px; align-items: end;
}
.cs-set-modal .cs-row-cell { min-width: 0; }
.cs-set-modal .cs-row-label {
  font-size: 11.5px; color: var(--ink-soft);
  margin-bottom: 4px; font-weight: 500;
}
.cs-set-modal .cs-row-cell .cs-select { margin-top: 0; }
.cs-set-modal .cs-row-arrow {
  font-size: 16px; color: var(--ink-soft);
  padding-bottom: 7px; user-select: none;
}

/* Empty-state placeholder inside a cs-block (e.g. before target board is picked) */
.cs-set-modal .cs-empty {
  width: 100%; box-sizing: border-box;
  font-size: 12.5px; color: var(--ink-soft);
  padding: 10px 12px; border: 1px dashed var(--line);
  border-radius: 6px; text-align: center; background: var(--bg);
}

/* 2. Title extras — chip-style multi-select */
.cs-set-modal .cs-chips {
  display: flex; flex-wrap: wrap; gap: 6px;
}
.cs-set-modal .cs-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 10px; border: 1px solid var(--line); border-radius: 14px;
  background: var(--card); cursor: pointer; font-size: 12px;
  transition: background .12s, border-color .12s;
}
.cs-set-modal .cs-chip:hover { border-color: var(--primary); }
.cs-set-modal .cs-chip input { display: none; }
.cs-set-modal .cs-chip.on {
  background: rgba(0, 115, 234, .08); border-color: var(--primary); color: var(--primary);
}
.cs-set-modal .cs-chip-name { font-weight: 500; }
.cs-set-modal .cs-chip-type {
  font-size: 10.5px; color: var(--ink-soft);
  padding: 1px 5px; border-radius: 8px; background: var(--bg);
}
.cs-set-modal .cs-chip.on .cs-chip-type { background: rgba(0,115,234,.12); color: var(--primary); }

/* 3. Color source — side-by-side option cards.
   Each card stacks: radio + title on top, swatch row below. */
.cs-set-modal .cs-color-list {
  display: grid; gap: 8px;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.cs-set-modal .cs-color-opt {
  display: flex; flex-direction: column; gap: 8px;
  padding: 10px 12px; border: 1px solid var(--line); border-radius: 6px;
  background: var(--card); cursor: pointer;
  transition: background .12s, border-color .12s;
}
.cs-set-modal .cs-color-opt:hover { border-color: var(--primary); }
.cs-set-modal .cs-color-opt.on {
  background: rgba(0, 115, 234, .06); border-color: var(--primary);
}
.cs-set-modal .cs-color-head {
  display: flex; align-items: center; gap: 6px;
  min-width: 0;
}
.cs-set-modal .cs-color-opt input {
  accent-color: var(--primary); margin: 0; flex-shrink: 0;
}
.cs-set-modal .cs-color-title {
  font-size: 12.5px; font-weight: 500; color: var(--ink);
  flex: 1; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cs-set-modal .cs-color-swatches {
  display: flex; flex-wrap: wrap; gap: 3px; align-items: center;
}
.cs-set-modal .cs-sw {
  display: inline-block; width: 14px; height: 14px; border-radius: 50%;
  border: 1px solid rgba(0, 0, 0, .08);
}
.cs-set-modal .cs-sw-empty { font-size: 11px; color: var(--ink-soft); }

/* Form settings modal — uses the shared cs-set-modal cards. These helpers
   live inside each .cs-block: a slider row, a colour-picker row, an image
   uploader row, etc. */
.cs-set-modal .cs-size-row {
  display: flex; align-items: center; gap: 12px;
}
.cs-set-modal .cs-size-row input[type="range"] {
  flex: 1; margin: 0; padding: 0; height: 4px; accent-color: var(--primary);
}
.cs-set-modal .cs-size-val {
  font-size: 12px; color: var(--ink-soft); min-width: 38px; text-align: right;
}
/* Generic "detail row" used by both the background-colour and text-colour
   sections — a colour swatch sits next to optional helper buttons/text. */
.cs-set-modal .cs-bg-detail {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: 8px;
}
.cs-set-modal .cs-bg-detail input[type="color"] {
  width: 38px; height: 30px; padding: 0; border: 1px solid var(--line);
  border-radius: 6px; cursor: pointer; background: var(--card);
}
.cs-set-modal .cs-bg-detail input[type="file"] { font-size: 12px; }
.cs-set-modal .form-bg-preview {
  width: 64px; height: 40px; border: 1px solid var(--line); border-radius: 4px;
  background-size: cover; background-position: center; background-color: var(--bg);
}

/* Share-board modal — per-user permission rows + link section. Everything
   inside cs-set-modal inherits font-family from the modal, and inputs use
   the same height/padding/font-size as .cs-select so the modal reads as
   one consistent surface. */
.cs-set-modal .sh-user-list {
  display: flex; flex-direction: column; gap: 4px;
  max-height: 280px; overflow-y: auto;
  font-size: 13px;
}
.cs-set-modal .sh-user-row {
  display: flex; align-items: center; gap: 12px;
  padding: 6px 8px; border-radius: 6px;
}
.cs-set-modal .sh-user-row:hover { background: var(--surface); }
.cs-set-modal .sh-user-info { flex: 1; min-width: 0; }
.cs-set-modal .sh-user-name {
  font-size: 13px; font-weight: 500; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cs-set-modal .sh-user-email { font-size: 11.5px; color: var(--ink-soft); }
.cs-set-modal .sh-perms { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.cs-set-modal .sh-perm {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12px; font-weight: 500; cursor: pointer; user-select: none;
  padding: 4px 10px; border-radius: 14px;
  background: var(--surface); color: var(--ink-soft); border: 1px solid transparent;
  transition: background .12s, border-color .12s, color .12s;
  margin: 0;
}
.cs-set-modal .sh-perm:hover { border-color: var(--primary); }
.cs-set-modal .sh-perm input {
  width: 13px; height: 13px; margin: 0; padding: 0; border: 0; border-radius: 0;
  accent-color: var(--primary); flex-shrink: 0;
}
.cs-set-modal .sh-perm:has(input:checked) {
  background: rgba(0,115,234,.12); color: var(--primary); border-color: rgba(0,115,234,.3);
}

/* Link section — inputs match the height + look of .cs-select */
.cs-set-modal .sh-link-url { display: flex; gap: 8px; margin-bottom: 10px; }
.cs-set-modal .sh-link-url input {
  flex: 1; height: 32px; padding: 0 10px;
  border: 1px solid var(--line); border-radius: 6px; background: var(--card);
  font-size: 13px; color: var(--ink); margin: 0;
  font-family: inherit;
}
.cs-set-modal .sh-pass-row {
  display: flex; gap: 8px; align-items: center; margin-bottom: 10px;
}
.cs-set-modal .sh-pass-row input[type="text"] {
  flex: 1; height: 32px; padding: 0 10px;
  border: 1px solid var(--line); border-radius: 6px; background: var(--card);
  font-size: 13px; color: var(--ink); margin: 0;
}
.cs-set-modal .sh-link-actions { display: flex; gap: 8px; justify-content: flex-end; }
.cs-set-modal .sh-views-head {
  font-size: 12px; color: var(--ink-soft); margin: 4px 0 6px;
}
.cs-set-modal .sh-views { margin-bottom: 10px; }

/* Master toggle that reveals/hides the whole link panel. Pill-shaped track
   with a sliding knob — drives state via :has() so no JS toggle classes. */
.cs-set-modal .sh-toggle {
  display: inline-flex; align-items: center; gap: 8px;
  cursor: pointer; user-select: none; margin: 0 0 10px;
  font-size: 13px; font-weight: 500; color: var(--ink);
}
.cs-set-modal .sh-toggle input {
  position: absolute; opacity: 0; pointer-events: none;
  width: 0; height: 0;
}
.cs-set-modal .sh-toggle-track {
  position: relative; display: inline-block;
  width: 34px; height: 18px; flex-shrink: 0;
  background: #d4d8e0; border-radius: 999px;
  transition: background .15s ease;
}
.cs-set-modal .sh-toggle-track::after {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 14px; height: 14px; border-radius: 50%;
  background: var(--card); box-shadow: 0 1px 2px rgba(0,0,0,.18);
  transition: transform .15s ease;
}
.cs-set-modal .sh-toggle:has(input:checked) .sh-toggle-track { background: var(--primary); }
.cs-set-modal .sh-toggle:has(input:checked) .sh-toggle-track::after { transform: translateX(16px); }
.cs-set-modal .sh-toggle-lbl { line-height: 18px; }

/* Settings modal — per-column row with two small toggles. Sits inside a
   .cs-block which already provides the card chrome, so no inner border. */
.form-set-list {
  display: flex; flex-direction: column;
  max-height: 320px; overflow-y: auto;
  margin-top: 4px;
}
.form-set-row {
  display: flex; align-items: center; gap: 14px;
  padding: 4px 10px; border-radius: 4px; font-size: 12.5px;
}
.form-set-row:hover { background: var(--surface); }
.form-set-name { flex: 1; }
.form-set-toggle {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12px; cursor: pointer; font-weight: 400;
}
.form-set-toggle input[type="checkbox"] { accent-color: var(--primary); width: 14px; height: 14px; margin: 0; }
/* Submit row in the form view — message on the left, button on the right */
.form-submit-row {
  display: flex; justify-content: space-between; align-items: center; gap: 12px;
}

/* Chip-style pickers in the form view — same visual language as filter chips */
.form-chips { display: flex; flex-wrap: wrap; gap: 4px; }
.form-chip {
  border: 1px solid transparent; padding: 4px 10px; border-radius: 12px;
  font-size: 11.5px; font-weight: 600; font-family: inherit; cursor: pointer;
  opacity: .55; color: #fff; transition: opacity .1s ease;
}
.form-chip:hover { opacity: .85; }
.form-chip.on { opacity: 1; box-shadow: 0 0 0 2px rgba(0,0,0,.18) inset; }
.form-chip-person {
  background: #e6efff; color: var(--ink); opacity: 1; border: 1px solid var(--line);
}
.form-chip-person.on { background: var(--primary); color: #fff; border-color: var(--primary); box-shadow: none; }
.form-people { display: flex; flex-direction: column; gap: 6px; }
.form-people-search {
  width: 100%; box-sizing: border-box; padding: 5px 8px;
  border: 1px solid var(--line); border-radius: 5px;
  font-family: inherit; font-size: 12px;
}
.form-people-search:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 2px rgba(87,155,252,.18); }
.form-people-list { display: flex; flex-wrap: wrap; gap: 4px; }
.form-stars { display: inline-flex; gap: 2px; }
.form-star {
  background: transparent; border: none; padding: 2px 4px;
  font-size: 22px; line-height: 1; cursor: pointer; color: #d8dde6;
  transition: color .1s ease;
}
.form-star:hover, .form-star.on { color: #ffcb00; }
/* Read-only formula display in form view — same border as inputs */
.ff-formula {
  padding: 5px 8px; border: 1px solid var(--line); border-radius: 5px;
  background: var(--surface); color: var(--ink); font-size: 12px;
  font-variant-numeric: tabular-nums; font-weight: 600;
  min-height: 26px; box-sizing: border-box;
}
.ff-formula.ff-formula-empty { color: var(--ink-soft); font-weight: 400; }

/* Inline validation feedback on form inputs */
.ff-input.ff-invalid {
  border-color: var(--red);
  box-shadow: 0 0 0 2px rgba(226,68,92,.15);
}
.ff-error {
  color: var(--red); font-size: 11.5px; font-weight: 400; margin: 0;
}

/* Multi-file upload field — native input + chip-style list of chosen files */
.ff-file-wrap { display: flex; flex-direction: column; gap: 6px; }
.ff-file {
  font-family: inherit; font-size: 12px;
  /* Match the height of the other inputs (.ff-input) so file fields don't
     stand taller than the rest — especially inside the layout-2 input-group.
     Horizontal padding matches .ff-input (8px); vertical spacing is handled by
     flex-centering so the upload button sits evenly inside the box. */
  height: 28px; box-sizing: border-box;
  padding: 0 8px;
  display: flex; align-items: center;
  border: 1px solid var(--line); border-radius: 5px;
  background: var(--card); cursor: pointer; overflow: hidden;
}
.ff-file::-webkit-file-upload-button {
  background: var(--surface); border: 1px solid var(--line); border-radius: 4px;
  padding: 3px 10px; font-size: 12px; cursor: pointer; margin-right: 8px;
  font-family: inherit;
}
.ff-file::-webkit-file-upload-button:hover { background: #eef0f3; }
.ff-file-list { display: flex; flex-wrap: wrap; gap: 4px; }
/* When no file is chosen yet the list is empty — hide it so the wrapper's
   column-gap doesn't add height and make the field taller than the others. */
.ff-file-list:empty { display: none; }
.ff-file-chip {
  background: #eef5ff; color: var(--primary);
  font-size: 11.5px; padding: 3px 9px; border-radius: 11px; font-weight: 600;
}

/* Gantt */
.gantt {
  background: var(--card); border: none; border-radius: 0;
  padding: 14px 18px;
  display: flex; flex-direction: column;
  min-height: 0;
  position: relative;
}
/* Divider between the label column and the timeline lives on the right edge
   of the sticky elements themselves — keeps it in the same stacking context
   as the column so nothing bleeds past it during horizontal scroll. */
.gantt-axis { display: flex; align-items: center; gap: 12px; font-size: 12px; font-weight: 600;
  border-bottom: 1px solid var(--line);
  flex-shrink: 0;
  /* Solid background + own stacking context so rows scrolling through the
     extended week-line bleed never peek above the axis baseline. */
  background: var(--card); position: relative; z-index: 4;
  /* Match the data row height exactly so the bg gradient cycles align. */
  min-height: 24px; height: 24px; box-sizing: border-box; }
/* No min-height: 0 — that would let flex shrink the rows below content size
   and trigger a vertical scrollbar inside the box. Default min-height: auto
   keeps the rows at content size so the chart grows downward without a
   v-scroll. overflow-y: auto stays so the sticky label column still has a
   scroll container to anchor against (h-axis is auto-coerced too per spec). */
.gantt-rows {
  flex: 1; overflow-y: auto;
  isolation: isolate;
  /* Row-divider lines as a repeating background. Axis + each data row are
     exactly 16px tall, so the gradient cycles cleanly from y=0 with no
     offset math — line lands on the bottom 1px of each 16px row. `local`
     attachment makes the bg scroll with the inner content so lines reach
     the full scroll width, not just the visible viewport. */
  background-image: repeating-linear-gradient(
    to bottom,
    transparent 0,
    transparent 23px,
    #f0f2f7 23px,
    #f0f2f7 24px
  );
  background-attachment: local;
  background-repeat: repeat;
}
/* Hide both scrollbars' chrome — the chart still scrolls/pans via trackpad /
   wheel / touch. width:0 hides the vertical bar, height:0 the horizontal one;
   scrollbar-width:none covers Firefox. */
.gantt-rows { scrollbar-width: none; -ms-overflow-style: none; }
.gantt-rows::-webkit-scrollbar { width: 0; height: 0; }
/* Spacer matches the .gantt-label column so the timeline header aligns with
   the rows below. Sticky so it stays pinned during horizontal scroll. */
.gantt-axis-spacer {
  width: 180px; flex-shrink: 0;
  position: sticky; left: 0;
  background: var(--card); z-index: 30;
  border-right: 1px solid var(--line-dark);
  align-self: stretch;
  /* Anchor the collapse toggle at the right edge of the spacer. */
  display: flex; align-items: center; justify-content: flex-end;
  padding-right: 4px;
  transition: width .15s ease;
}
/* Chevron button that hides / shows the left label column. */
.gantt-collapse-btn {
  width: 22px; height: 22px; border: 1px solid var(--line);
  background: var(--card); color: var(--ink-soft);
  border-radius: 4px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: color .12s, border-color .12s;
}
.gantt-collapse-btn:hover { color: var(--primary); border-color: var(--primary); }
.gantt-collapse-btn svg { transform: rotate(90deg); transition: transform .15s ease; }
.gantt.gantt-collapsed .gantt-collapse-btn svg { transform: rotate(-90deg); }
/* Collapsed state — label column reduced to a thin strip just wide enough
   for the chevron button. The bars and axis-timeline take the recovered space. */
.gantt.gantt-collapsed .gantt-axis-spacer,
.gantt.gantt-collapsed .gantt-label {
  width: 28px;
}
.gantt.gantt-collapsed .gantt-label {
  font-size: 0; padding-right: 0;            /* hide name; just keep the divider strip */
}
/* The timeline strip: start/end labels at the edges, weeks absolutely
   positioned at their proportional spot. min-width forces a horizontal
   scroll when the date range is large so days don't get squashed. */
.gantt-axis-timeline {
  flex: 1; position: relative;
  display: flex; justify-content: space-between; align-items: center;
  min-height: 18px;
  min-width: var(--timeline-min-w, 0);
}
.gantt-axis-timeline .ga-start,
.gantt-axis-timeline .ga-end { color: var(--ink); }
.gantt-axis-timeline .ga-count { color: var(--ink-soft); font-weight: 500; margin-left: 4px; }
.gantt-axis-timeline .ga-week {
  position: absolute; top: 50%; transform: translate(-50%, -50%);
  font-size: 11px; font-weight: 600; color: var(--ink-soft);
  padding: 1px 6px; border-radius: 10px; background: var(--bg);
  white-space: nowrap; pointer-events: auto;
}
/* Row padding removed so the .gantt-label's right-border divider stays
   continuous between consecutive rows. Track itself provides visual height.
   border-bottom paints the divider through the label column too (the bg
   gradient on .gantt-rows is hidden behind the labels' white sticky bg).
   Softer line colour so the grid stays in the background. */
.gantt-row { display: flex; align-items: stretch; gap: 12px; padding: 0; position: relative;
  min-height: 24px;
  border-bottom: 1px solid #f0f2f7; }
/* Keep the divider on the last row too — its bottom line was being removed. */
/* Horizontal row dividers as repeating background — painted on .gantt-rows
   with background-attachment: local so they scroll with the inner content
   and reach the full scrollable width (not just the visible viewport). */
.gantt-label { width: 180px; font-size: 13px; font-weight: 600; flex-shrink: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  /* Pin the name column when the chart (or page) is scrolled horizontally.
     High z-index keeps it above every absolute child of .gantt-track during
     scroll. Border on the right is the column/track divider — paints with
     the label itself so it can never bleed through. */
  position: sticky; left: 0;
  background: var(--card); z-index: 20;
  padding-right: 6px;
  border-right: 1px solid var(--line-dark);
  /* Stretch over the row's full content height so the divider segments touch
     between rows (rows have padding: 5px 0 around the label/track strip). */
  align-self: stretch;
  display: flex; align-items: center;
}
/* Track creates its own stacking context so bars/week-lines/names stay
   contained — without this the absolutely-positioned children can outrank
   the sticky label column. min-width matches the axis-timeline so rows
   stay aligned with the header markers when the chart scrolls horizontally. */
.gantt-track { flex: 1; position: relative; height: 22px; border-radius: 6px;
  z-index: 0;
  min-width: var(--timeline-min-w, 0); }
/* Faint vertical lines marking each ISO week boundary across the track.
   Extends past the track top/bottom by the row's full padding so lines on
   consecutive rows visually connect into one continuous column. */
.gantt-week-line {
  position: absolute; top: -5px; bottom: -5px; width: 1px;
  background: var(--line-dark);
  opacity: .35; pointer-events: none;
  z-index: 0;
}
.gantt-bar { position: absolute; top: 4px; height: 14px; border-radius: 7px;
  cursor: pointer; z-index: 2;
  /* The name now sits outside the bar, so we don't need flex/padding here. */ }
/* Baseline / background bar — a second date range shown in grey, nudged down
   so the main bar visibly overlaps it (e.g. plan vs actual). Purely visual. */
.gantt-bar-bg {
  position: absolute; top: 11px; height: 11px; border-radius: 7px;
  background: #6b7384; opacity: .85; z-index: 1; pointer-events: none;
}
/* Drag-to-move / resize (editors only). Grab the bar to shift its dates, or
   grab an edge handle to extend/shorten. */
body:not(.bp-no-edit):not(.is-guest) .gantt-bar { cursor: grab; touch-action: none; }
.gantt-bar.gbar-dragging { cursor: grabbing; opacity: .9; box-shadow: 0 2px 8px rgba(0,0,0,.25); }
.gbar-h { position: absolute; top: -2px; bottom: -2px; width: 10px; z-index: 3; cursor: ew-resize; touch-action: none; }
.gbar-h-l { left: -4px; }
.gbar-h-r { right: -4px; }
/* Item name sits flush against the bar's right edge on the same line.
   No right constraint — names extend past the track for items at the far
   right of the timeline, which creates horizontal overflow and powers the
   sticky-column freeze + horizontal scroll. */
.gantt-bar-name {
  position: absolute; top: 50%; transform: translateY(-50%);
  font-size: 12.5px; font-weight: 400; color: var(--ink);
  white-space: nowrap; cursor: pointer;
  overflow: hidden; text-overflow: ellipsis;
  display: inline-flex; align-items: center; gap: 4px;
  /* Above the grey baseline bar (z1) and main bar (z2) so the item details
     stay readable even where the background bar runs underneath them. */
  z-index: 3;
}
.gantt-bar-name .gbn-part {
  display: inline-flex; align-items: center; gap: 3px; min-width: 0;
}
.gantt-bar-name .gbn-ic {
  display: inline-flex; align-items: center; color: var(--ink-soft); opacity: .85;
}
.gantt-bar-name .gbn-ic svg { width: 11px; height: 11px; }
.gantt-bar-name .gbn-v { overflow: hidden; text-overflow: ellipsis; }
/* "Today" marker — red line running down every track, with a triangle flag
   pinned to the axis header at the same x. */
.gantt-today-line {
  position: absolute; top: 0; bottom: 0; left: 0; width: 2px;
  margin-left: -1px; background: #e2445c; z-index: 4; pointer-events: none;
}
.gantt-today-flag {
  position: absolute; bottom: -1px; transform: translateX(-50%);
  width: 0; height: 0;
  border-left: 5px solid transparent; border-right: 5px solid transparent;
  border-top: 8px solid #e2445c;          /* downward-pointing triangle */
  z-index: 6; pointer-events: none;
}
.gantt-bar-name .gbn-sep { color: var(--ink-soft); opacity: .55; }

/* Doc */
.doc-wrap { max-width: 820px; }
.doc-editor {
  width: 100%; min-height: 460px; padding: 26px; font-size: 15px; line-height: 1.7;
  border: 1px solid var(--line); border-radius: 10px; font-family: inherit;
  resize: vertical; background: var(--card);
}
.doc-editor:focus { outline: none; border-color: var(--primary); }

/* ── Text-editor (lightweight rich text) view ──────────────────────────── */
/* Full-bleed: the editor fills the whole view area (width + height). */
/* The "desk" the paper floats on (grey, unlike white --bg). A flex column that
   fills the whole view area so the bottom page bar can be pushed to the very
   bottom even when the (zoomed-down) paper is shorter than the viewport. */
.te-wrap { max-width: none; width: 100%; min-width: 0; background: var(--surface); display: flex; flex-direction: column; flex: 1; }
/* Text-editor view fills the whole area — no board-body padding/margin around
   it (the two-class selector beats .board-body incl. its mobile override). */
.board-body.te-body { padding: 0; display: flex; flex: 1 0 auto; }
.te-toolbar {
  /* Single row that slides horizontally when the buttons exceed the screen
     width (instead of wrapping onto extra rows). */
  display: flex; flex-wrap: nowrap; align-items: center; gap: 2px;
  padding: 6px 8px; background: var(--surface);
  border: none; box-shadow: 0 3px 8px rgba(0, 0, 0, .12);   /* floating: shadow below, no bottom line */
  border-radius: 0; position: sticky; top: 0; z-index: 2;
  overflow-x: auto; overflow-y: hidden; scrollbar-width: thin;
}
/* Keep every control at its natural size so the bar overflows → scrolls,
   rather than squashing buttons to fit. */
.te-toolbar > * { flex-shrink: 0; }
.te-toolbar::-webkit-scrollbar { height: 6px; }
.te-toolbar::-webkit-scrollbar-thumb { background: var(--line-dark, #c7ccd8); border-radius: 3px; }
.te-btn {
  min-width: 30px; height: 28px; padding: 0 8px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid transparent; border-radius: 6px;
  background: transparent; color: var(--ink); cursor: pointer;
  font-size: 13px; font-family: inherit; line-height: 1;
}
.te-btn:hover { background: var(--hover, #eef1f6); border-color: var(--line); }
.te-btn.te-on { background: var(--primary); color: #fff; border-color: var(--primary); }   /* tool active (e.g. hide-border) */
.te-btn[data-cmd="bold"] { font-weight: 800; }
.te-btn[data-cmd="italic"] { font-style: italic; }
.te-btn[data-cmd="underline"] { text-decoration: underline; }
.te-btn[data-cmd="strikeThrough"] { text-decoration: line-through; }
.te-sep { width: 1px; height: 18px; margin: 0 2px; background: var(--line); }
/* Font family / size / colour controls in the toolbar. */
.te-font, .te-fsize {
  height: 28px; border: 1px solid var(--line); border-radius: 6px;
  background: var(--card); color: var(--ink); font-size: 12.5px; font-family: inherit;
}
.te-font { max-width: 140px; padding: 0 4px; cursor: pointer; }
.te-fsize { width: 50px; padding: 0 6px; }
.te-fcolor {
  width: 22px; height: 22px; padding: 1px;
  border: 1px solid var(--line); border-radius: 50%; background: var(--card); cursor: pointer;
}
/* Fill-background control: paint-bucket icon + colour swatch. */
.te-bg-ctl {
  display: inline-flex; align-items: center; gap: 3px; height: 26px;
  padding: 0 3px 0 5px; border: 1px solid var(--line); border-radius: 13px;
  background: var(--card); cursor: pointer; color: var(--ink-soft);
}
.te-bg-ctl svg { width: 14px; height: 14px; }
.te-bg-ctl input[type=color] { width: 16px; height: 16px; padding: 0; border: none; background: none; cursor: pointer; }
/* Paint-bucket = apply current colour; it sits flush inside the pill.
   The clear-fill (×) button lives in the same pill, split off by a thin rule. */
.te-bg-apply { min-width: 0; width: 20px; height: 20px; padding: 0; border: none; background: none; border-radius: 5px; }
.te-bg-apply:hover { background: var(--hover, #eef1f6); border-color: transparent; }
.te-bg-div { width: 1px; height: 16px; background: var(--line); flex-shrink: 0; }
/* Zoom control — a single pill (same chrome as the fill-colour .te-bg-ctl):
   [ − | 100% | + ]. The − / + reuse .te-bg-apply; the read-out resets to 100%. */
.te-zoom-ctl {
  display: inline-flex; align-items: center; gap: 3px; height: 26px;
  padding: 0 3px; border: 1px solid var(--line); border-radius: 13px;
  background: var(--card); color: var(--ink-soft);
}
.te-zoom-lbl {
  min-width: 40px; height: 22px; padding: 0 4px; border: none; background: none;
  font-size: 12px; font-weight: 600; color: var(--ink-soft);
  font-variant-numeric: tabular-nums; text-align: center;
}
.te-zoom-lbl:hover { background: var(--hover, #eef1f6); border-color: transparent; }
/* Round colour swatches (circle instead of square). */
.te-fcolor::-webkit-color-swatch-wrapper, .te-bg-ctl input[type=color]::-webkit-color-swatch-wrapper { padding: 0; }
.te-fcolor::-webkit-color-swatch, .te-bg-ctl input[type=color]::-webkit-color-swatch { border: 1px solid var(--line); border-radius: 50%; }
.te-fcolor::-moz-color-swatch, .te-bg-ctl input[type=color]::-moz-color-swatch { border: 1px solid var(--line); border-radius: 50%; }
.te-editor {
  /* A sheet of paper floating on the desk (.te-wrap): sized at its REAL paper
     dimensions (width + min-height in mm, set inline per page) so the chosen
     size/orientation shows exactly. No inner scrollbar — the sheet grows taller
     with content. Centered on the desk; wider papers overflow → the page scrolls. */
  box-sizing: border-box;
  margin: 18px auto;
  font-size: 15px; line-height: 1.7;
  border: none; border-radius: 2px;
  outline: none; overflow: visible;
  background: var(--card);
  box-shadow: 0 3px 18px rgba(0, 0, 0, .16), 0 1px 3px rgba(0, 0, 0, .10);
}
/* Wrapper for the sheet + the page-break gap overlays that sit on top of it.
   Grows to fill the desk so the pagebar below it stays pinned to the bottom
   when the paper is short (e.g. zoomed to 40%). */
.te-sheet-wrap { position: relative; flex: 1 0 auto; }
.te-page-gaps { position: absolute; inset: 0; pointer-events: none; z-index: 3; }
/* Each gap covers the content at a page boundary with the desk colour (so a
   table/paragraph crossing the boundary reads as two separate sheets), with a
   soft shadow on the page above/below for a lifted-sheet look. Height = --te-gap. */
.te-page-gap {
  position: absolute; height: 0;   /* left/width set inline to match the paper */
  border-top: 1.5px dashed rgba(0, 0, 0, .3);   /* page-break line where content exceeds a page */
}
.te-editor.te-readonly { border-radius: 2px; }
.te-editor:focus { border-color: var(--line); }   /* keep the border colour on focus (don't tint) */
/* Placeholder when empty (contenteditable has no native placeholder). */
.te-editor:empty::before { content: attr(data-placeholder); color: var(--ink-soft); }
/* Shared typography for both the editor and the read-only render. */
.te-content h1 { font-size: 1.7em; margin: .4em 0 .3em; }
.te-content h2 { font-size: 1.35em; margin: .4em 0 .3em; }
.te-content p { margin: 0 0 .6em; }
.te-content ul, .te-content ol { margin: 0 0 .6em; padding-left: 1.6em; }
.te-content a { color: var(--primary); text-decoration: underline; }
.te-content img { max-width: 100%; height: auto; }
.te-content table { border-collapse: collapse; }
.te-content table td, .te-content table th { padding: 3px 6px; min-width: 24px; vertical-align: top; }
/* Bottom page bar — the card's footer (seamless with the editor above), pinned
   to the bottom while scrolling. Tabs use the same underline style as the board
   view tabs. The editor's bottom border is the divider (page bar has none). */
.te-pagebar {
  position: sticky; bottom: 0; z-index: 3;
  display: flex; align-items: center; gap: 2px;
  padding: 0 8px; overflow-x: auto; scrollbar-width: none;
  background: var(--surface);
  border: 1px solid var(--line); border-radius: 0;
  box-shadow: 0 -2px 6px rgba(0, 0, 0, .04);
}
.te-pagebar::-webkit-scrollbar { display: none; }
/* Save-status indicator at the front of the page bar: file (idle) → spinner
   (saving) → check-circle (saved). One icon shows per data-state. */
.te-save-ind { display: inline-flex; align-items: center; flex-shrink: 0; padding: 0 8px; }
.te-save-ind .tsi { display: none; align-items: center; }
.te-save-ind .tsi svg { width: 16px; height: 16px; }
.te-save-ind[data-state="idle"]   .tsi-idle   { display: inline-flex; color: var(--ink-soft); }
.te-save-ind[data-state="saving"] .tsi-saving { display: inline-flex; color: var(--primary); }
.te-save-ind[data-state="saved"]  .tsi-saved  { display: inline-flex; color: #00c875; }
.te-save-ind .tsi-saving svg { animation: te-spin .8s linear infinite; }
@keyframes te-spin { to { transform: rotate(360deg); } }
.te-page-tab {
  flex-shrink: 0; border: none; background: transparent; cursor: pointer;
  padding: 8px 14px; font-size: 13px; font-weight: 600; font-family: inherit;
  color: var(--ink-soft); border-bottom: 3px solid transparent; white-space: nowrap;
}
.te-page-tab:hover { color: var(--ink); }
.te-page-tab.active { color: var(--ink); border-bottom-color: var(--primary); }
/* Delete-page ×: faded until the tab is hovered, reddens on its own hover. */
.te-page-x {
  margin-left: 7px; font-size: 15px; line-height: 1; color: var(--ink-soft);
  opacity: 0; transition: opacity .12s ease, color .12s ease;
}
.te-page-tab:hover .te-page-x, .te-page-tab.active .te-page-x { opacity: .6; }
.te-page-x:hover { opacity: 1 !important; color: var(--red, #e2445c); }
.te-page-name { outline: none; }
.te-page-name[contenteditable="true"] { background: var(--card); border-radius: 3px; padding: 0 4px; }
.te-page-add {
  flex-shrink: 0; border: none; background: transparent; color: var(--ink-soft); cursor: pointer;
  padding: 8px 10px; border-radius: 6px; display: inline-flex; align-items: center;
}
.te-page-add:hover { background: var(--card); color: var(--primary); }
/* Data token — an atomic {column} placeholder (contenteditable=false), so one
   backspace removes the whole chip rather than a single character. */
.te-token {
  background: color-mix(in srgb, var(--primary) 13%, transparent);
  color: var(--primary); border-radius: 4px; padding: 0 4px;
  white-space: nowrap; cursor: default;
}
/* Insert-data column picker (modal). */
.te-data-modal { min-width: 320px; }
.te-data-search {
  width: 100%; height: 32px; margin: 8px 0; padding: 0 10px;
  border: 1px solid var(--line); border-radius: 6px;
  background: var(--card); color: var(--ink); font-family: inherit; font-size: 13px;
}
.te-data-list { max-height: 320px; overflow-y: auto; display: flex; flex-direction: column; gap: 2px; }
.te-data-opt {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  padding: 7px 9px; border: 1px solid transparent; border-radius: 6px;
  background: transparent; color: var(--ink); cursor: pointer; font-size: 13px; font-family: inherit;
}
.te-data-opt:hover { background: var(--hover, #eef1f6); border-color: var(--line); }
.te-data-ico { display: inline-flex; color: var(--ink-soft); flex-shrink: 0; }
.te-data-ico svg { width: 15px; height: 15px; }
.te-data-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* View mode — the resolved data shows IN the editor (locked). A banner takes
   the toolbar's place with an exit button. */
.te-editor.te-viewing { cursor: default; background: var(--card); }
/* Resolved token value (view mode): keep formatted values on the SAME line as
   the surrounding text. Mirrors the .idet-val treatment — every cell renderer
   (including the block-level .cv-text and .cv-longtext) goes inline so the
   value flows with the surrounding text and wraps naturally instead of
   dropping onto its own line. */
.te-token-val { display: inline-block; vertical-align: middle; }
.te-token-val .cv-status, .te-token-val .cv-status-inline,
.te-token-val .cv-tag, .te-token-val .cv-avatar,
.te-token-val .cv-chip, .te-token-val .cv-link,
.te-token-val .cv-num, .te-token-val .cv-rating, .te-token-val .cv-date,
.te-token-val .cv-check, .te-token-val .cv-formula, .te-token-val .cv-text,
.te-token-val .cv-longtext, .te-token-val div, .te-token-val p {
  display: inline-block; vertical-align: middle; margin: 0;
}
/* inline-flex (NOT inline): keeps the people value on the same row AND lets
   html2canvas paint the avatar <img> in PDF export — display:inline here made
   it drop the image, leaving an empty chip. */
.te-token-val .cv-people, .te-token-val .cv-people-full { display: inline-flex; vertical-align: middle; }
.te-view-banner {
  position: sticky; z-index: 4;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 6px 8px;   /* same box as .te-toolbar → equal height */
  background: color-mix(in srgb, var(--primary) 12%, var(--card));
  border: none; box-shadow: 0 3px 8px rgba(0, 0, 0, .12);   /* floating: shadow below, no bottom line */
  color: var(--primary); font-size: 13px; font-weight: 600;
}
.te-vb-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding-left: 4px; }
/* PDF export — the content renders on a visible white overlay so html2canvas
   captures it, then the overlay is removed when the file is saved. */
.te-pdf-overlay {
  position: fixed; inset: 0; z-index: 3000; overflow: auto;
  background: #fff; display: flex; flex-direction: column; align-items: center; padding: 16px;
}
.te-pdf-note { font-size: 13px; color: var(--ink-soft); margin-bottom: 10px; }
.te-pdf-holder { background: #fff; color: #23272f; }
/* Kill trailing bottom space so the content doesn't tip onto an extra page. */
.te-pdf-holder > :last-child { margin-bottom: 0 !important; }
.te-pdf-holder p:last-child { margin-bottom: 0 !important; }
.te-vb-actions { display: inline-flex; align-items: center; gap: 3px; flex-shrink: 0; }
/* Banner buttons sit on a white background (the active "format" one keeps the
   primary highlight). :not(.te-on) beats .te-btn.te-on so it stays primary. */
.te-view-banner .te-btn:not(.te-on) { background: var(--card); border-color: var(--line); }
.te-view-banner .te-btn:not(.te-on):hover { background: var(--hover, #eef1f6); }
/* Floating "+" handles to add a row / column (shown on table hover).
   The plus is drawn with two centred bars (pseudo-elements) instead of a text
   glyph, so it sits exactly in the middle of the circle in any font. */
.te-add-handle {
  position: fixed; z-index: 60; display: none;
  width: 22px; height: 22px; padding: 0; font-size: 0;
  border: 1px solid var(--line); border-radius: 50%;
  background: var(--card); color: var(--primary); cursor: pointer;
  box-shadow: 0 1px 4px rgba(0, 0, 0, .18);
}
.te-add-handle::before, .te-add-handle::after {
  content: ''; position: absolute; left: 50%; top: 50%;
  background: currentColor; border-radius: 1px;
  transform: translate(-50%, -50%);
}
.te-add-handle::before { width: 11px; height: 2px; }   /* horizontal bar */
.te-add-handle::after  { width: 2px; height: 11px; }   /* vertical bar */
.te-add-handle:hover { background: var(--primary); color: #fff; border-color: var(--primary); }
/* Delete-row / delete-column handles — a red "−" circle. */
.te-del-handle {
  position: fixed; z-index: 60; display: none;
  width: 22px; height: 22px; padding: 0; font-size: 0;
  border: 1px solid var(--line); border-radius: 50%;
  background: var(--card); color: var(--red, #e2445c); cursor: pointer;
  box-shadow: 0 1px 4px rgba(0, 0, 0, .18);
}
.te-del-handle::before {
  content: ''; position: absolute; left: 50%; top: 50%;
  width: 11px; height: 2px; background: currentColor; border-radius: 1px;
  transform: translate(-50%, -50%);
}
.te-del-handle:hover { background: var(--red, #e2445c); color: #fff; border-color: var(--red, #e2445c); }
/* ⋯ table-settings handle (border size + colour popup). */
.te-tbl-menu {
  position: fixed; z-index: 60; display: none;
  width: 22px; height: 22px; padding: 0;
  align-items: center; justify-content: center;
  border: 1px solid var(--line); border-radius: 50%;
  background: var(--card); color: var(--ink); cursor: pointer;
  font-size: 15px; line-height: 1; box-shadow: 0 1px 4px rgba(0, 0, 0, .18);
}
.te-tbl-menu:hover { background: var(--primary); color: #fff; border-color: var(--primary); }
/* Icon-only toolbar button (e.g. insert table). */
.te-btn-ico { padding: 0 7px; }
.te-btn-ico svg { width: 15px; height: 15px; display: block; }
/* Insert-table modal: compact fields (rows/cols, then border size/colour). */
.te-tbl-modal { width: 300px; }
.te-tbl-fields { display: flex; gap: 14px; margin-bottom: 10px; }
.te-tbl-fields label { flex: 1; display: block; font-weight: 600; font-size: 13px; }
.te-tbl-fields input { width: 100%; box-sizing: border-box; margin-top: 4px; padding: 6px 8px; border: 1px solid var(--line); border-radius: 6px; font-size: 13px; background: var(--card); color: var(--ink); }
.te-tbl-fields input[type=color] { height: 34px; padding: 2px; cursor: pointer; }
.te-tbl-fields select { width: 100%; box-sizing: border-box; margin-top: 4px; padding: 6px 8px; border: 1px solid var(--line); border-radius: 6px; font-size: 13px; font-family: inherit; background: var(--card); color: var(--ink); cursor: pointer; }
.te-pm-sub { font-size: 12px; font-weight: 600; color: var(--ink-soft); margin: 2px 0 4px; }

/* Canvas */
.canvas { position: relative; min-height: 640px; background: var(--card);
  background-image: radial-gradient(var(--line) 1px, transparent 1px);
  background-size: 22px 22px; border: 1px solid var(--line); border-radius: 10px; overflow: hidden; }
.canvas-card {
  position: absolute; width: 200px; background: var(--card); border: 1px solid var(--line);
  border-top: 4px solid var(--primary); border-radius: 8px; padding: 10px 12px;
  font-weight: 600; font-size: 13px; cursor: grab; box-shadow: 0 2px 8px rgba(0,0,0,.08);
  user-select: none;
}
.canvas-card.dragging { cursor: grabbing; box-shadow: 0 8px 22px rgba(0,0,0,.2); z-index: 5; }

/* Dashboard — customizable widget grid */
.dash-grid {
  display: grid; grid-template-columns: repeat(12, 1fr); gap: 16px;
  padding: 4px;
}
.dash-empty {
  grid-column: span 12; text-align: center; padding: 60px 20px;
  color: var(--ink-soft); background: var(--card); border: 1px dashed var(--line); border-radius: 12px;
}
.dash-empty p { margin-top: 10px; font-size: 14px; }
.dash-widget {
  background: var(--card); border: none; border-radius: 10px;
  box-shadow: 0 0 0 1px rgba(0,0,0,.04), 0 1px 3px rgba(0,0,0,.10), 0 2px 6px rgba(0,0,0,.05);
  padding: 14px 16px; display: flex; flex-direction: column;
  grid-column: span 6; min-height: 280px;
}
.dash-widget.dw-kpi { grid-column: span 3; min-height: 0; height: auto; position: relative; padding: 10px 14px; }
.dash-widget.dw-bar, .dash-widget.dw-hbar, .dash-widget.dw-line { grid-column: span 8; }
.dash-widget.dw-donut, .dash-widget.dw-pie, .dash-widget.dw-gauge { grid-column: span 4; }
.dash-widget.dw-table, .dash-widget.dw-progress { grid-column: span 6; }
.dash-widget.dw-water { grid-column: span 4; }
/* Water tank — animated SVG centred, with a level read-out caption below */
.dash-widget.dw-water .dw-body { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; }
.dw-tank { display: flex; align-items: center; justify-content: center; width: 100%; min-height: 0; }
.dw-tank .wt-svg { width: 100%; max-width: 168px; height: auto; }
.dw-water-cap { text-align: center; line-height: 1.15; }
.dw-water-pct { font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums; }
.dw-water-sub { display: block; font-size: 11px; color: var(--ink-soft); margin-top: 1px; font-variant-numeric: tabular-nums; }
.dash-widget.dw-progress { min-height: 0; height: auto; }
/* Gauge wants room for the semicircle plus the centre text — a touch
   shorter than the other circular charts since only the top half is drawn. */
.dash-widget.dw-gauge .dw-body { min-height: 200px; }

/* Stacked progress chart — every bucket lives in ONE horizontal track,
   split into coloured segments whose widths sum to 100%. A legend below
   lists each bucket's colour, label, value and percentage. */
.dw-progress {
  display: flex; flex-direction: column; gap: 12px;
  /* Left padding matches the visual indent other widget types get from
     their Y-axis label area, so the bar's left edge lines up with the
     chart bars in neighbouring cards. */
  padding: 4px 9px 4px 12px;
}
.dw-prog-total {
  font-size: 24px; font-weight: 700; color: var(--ink);
  letter-spacing: -0.015em; line-height: 1.1;
}
/* Battery-style track — pill with a small cap on the right echoing a
   physical battery silhouette + inner shadow so segments read as filling
   the cell rather than floating above it. */
.dw-prog-stack {
  position: relative;
  display: flex; width: 100%; height: 56px;
  border-radius: 12px; overflow: hidden; background: var(--surface);
  border: 2px solid var(--line);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, .07);
}
/* The battery cap — small tab on the right edge */
.dw-prog-stack::after {
  content: ""; position: absolute;
  right: -9px; top: 50%; transform: translateY(-50%);
  width: 8px; height: 22px;
  background: var(--line); border-radius: 0 3px 3px 0;
}
.dw-progress { padding-right: 9px;  /* leave room for the cap */ }
.dw-prog-seg {
  display: flex; align-items: center; justify-content: center;
  height: 100%; min-width: 0;
  color: #fff; font-size: 15px; font-weight: 700;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .2);
  overflow: hidden;
  transition: width .25s ease;
}
.dw-prog-seg + .dw-prog-seg {
  /* hairline divider between adjacent segments — keeps similar colours readable */
  box-shadow: inset 1px 0 0 rgba(255, 255, 255, .18);
}
/* Hide the inside-segment percentage when the segment is too narrow to
   fit the 15px text legibly — the legend below already shows it. */
.dw-prog-seg-pct { white-space: nowrap; }
.dw-prog-seg[style*="width:0"] .dw-prog-seg-pct,
.dw-prog-seg[style*="width:1"] .dw-prog-seg-pct,
.dw-prog-seg[style*="width:2"] .dw-prog-seg-pct,
.dw-prog-seg[style*="width:3"] .dw-prog-seg-pct,
.dw-prog-seg[style*="width:4."] .dw-prog-seg-pct,
.dw-prog-seg[style*="width:5."] .dw-prog-seg-pct,
.dw-prog-seg[style*="width:6."] .dw-prog-seg-pct,
.dw-prog-seg[style*="width:7."] .dw-prog-seg-pct { opacity: 0; }
.dw-prog-legend {
  display: flex; flex-wrap: wrap; gap: 6px 14px;
  font-size: 12px;
}
.dw-prog-leg {
  display: inline-flex; align-items: center; gap: 6px;
  min-width: 0;
}
.dw-prog-leg-dot {
  width: 10px; height: 10px; border-radius: 3px; flex-shrink: 0;
}
.dw-prog-leg-name {
  color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 160px;
}
.dw-prog-leg-val {
  color: var(--ink-soft); font-variant-numeric: tabular-nums;
}
.dw-head {
  display: flex; align-items: center; gap: 8px;
  margin-bottom: 10px;
}
.dw-title { font-size: 14px; font-weight: 700; flex: 1; cursor: pointer; }
.dw-title:hover { color: var(--primary); }
/* Home dashboard: only the widget title links to its source board view
   (added by home.js to .dw-title / .dw-kpi-label). The card body is inert.
   On hover the whole title (text + open icon) turns the system primary colour —
   no underline. */
.dw-title-link { cursor: pointer; }
.dw-title-link:hover { color: var(--primary); text-decoration: none; }
.dw-open-ico { display: inline-flex; vertical-align: middle; margin-left: 5px; opacity: .55; }
.dw-open-ico .icon { width: 13px; height: 13px; }
.dw-title-link:hover .dw-open-ico { opacity: 1; }
.dw-menu {
  cursor: pointer; color: var(--ink-soft); padding: 2px;
  border-radius: 4px; line-height: 0;
}
.dw-menu:hover { background: #eef0f3; color: var(--ink); }
.dw-body { flex: 1; position: relative; min-height: 200px; }
/* KPI body collapses to its content — no chart canvas to reserve space for */
.dash-widget.dw-kpi .dw-body { min-height: 0; position: static; align-self: stretch; }
/* KPI card — coloured icon chip + label + big number, no header bar.
   FIXED size (same on every card, never scales/shrinks) but sized big enough
   to match the height of the 3-line text block beside it. */
.dw-kpi { display: flex; align-items: center; justify-content: flex-start; gap: 10px; height: 100%; width: 100%; }
.dw-kpi-ico {
  flex-shrink: 0; width: 64px; height: 64px; border-radius: 14px;
  display: flex; align-items: center; justify-content: center;
}
.dw-kpi-ico .icon { width: 32px; height: 32px; }
.dw-kpi-main { min-width: 0; }
.dw-kpi-label {
  font-size: 11px; font-weight: 700; letter-spacing: .03em; text-transform: uppercase;
  color: var(--ink-soft); margin-bottom: 1px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dw-kpi-num { font-size: 22px; font-weight: 800; color: var(--ink); line-height: 1.1; font-variant-numeric: tabular-nums; }
.dw-kpi-cap { color: var(--ink-soft); font-size: 11px; margin-top: 1px; }
.dw-kpi-menu { position: absolute; top: 8px; right: 10px; opacity: .55; transition: opacity .12s; }
.dash-widget.dw-kpi:hover .dw-kpi-menu { opacity: 1; }

/* Icon picker grid in the widget modal */
.dw-icon-grid {
  display: flex; flex-wrap: wrap; gap: 6px;
  margin-top: 6px; max-height: 150px; overflow-y: auto;
  padding: 8px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface);
}
.dw-ico-opt {
  width: 34px; height: 34px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--line); border-radius: 8px; background: var(--card);
  color: var(--ink-soft); cursor: pointer; transition: border-color .1s, color .1s, background .1s;
}
.dw-ico-opt:hover { border-color: var(--primary); color: var(--primary); }
.dw-ico-opt.sel { border-color: var(--primary); background: var(--primary); color: #fff; }
.dw-ico-opt .icon { width: 18px; height: 18px; }
.dw-ico-none { font-size: 15px; font-weight: 700; }
.dash-widget canvas { max-height: 320px; }

/* ---------- Home dashboard ---------- */
/* Chart picker modal (gear) */
.hw-search {
  width: 100%; box-sizing: border-box; margin: 2px 0 10px;
  padding: 8px 12px; border: 1px solid var(--line); border-radius: 8px;
  font: inherit; background: var(--bg); color: var(--ink);
}
.hw-greet-lbl { display: block; font-size: 13px; font-weight: 600; color: var(--ink); margin-bottom: 2px; }
.hw-list { max-height: 48vh; overflow-y: auto; padding-right: 4px; }
.hw-group { margin-bottom: 12px; }
.hw-group-head {
  font-size: 12px; font-weight: 700; color: var(--ink-soft);
  text-transform: uppercase; letter-spacing: .03em; margin-bottom: 5px;
}
/* `.modal label{display:block}` + `.modal input{display:block;width:100%}`
   would stack the checkbox above the text — beat both with higher specificity
   so the row stays inline. */
.modal label.hw-opt {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border: 1px solid var(--line); border-radius: 8px;
  cursor: pointer; margin-bottom: 6px; font-weight: 400;
  transition: border-color .1s, background .1s;
}
.hw-opt:hover { border-color: var(--primary); background: var(--surface); }
.hw-opt .hw-cb,
.hw-opt .hw-cal-cb { width: auto; margin: 0; flex-shrink: 0; }
.hw-type {
  flex-shrink: 0; font-size: 11px; font-weight: 700; color: var(--primary);
  background: var(--surface); border-radius: 5px; padding: 2px 7px;
}
.hw-title { font-weight: 600; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hw-src { margin-left: auto; flex-shrink: 0; font-size: 12px; color: var(--ink-soft); }
/* Merged home calendar section */
.home-cal-sec { margin-top: 18px; }
.home-sec-head { display: flex; align-items: center; gap: 8px; font-size: 15px; font-weight: 700; color: var(--ink); margin: 0 0 10px; }
.home-cal-sec .fc-wrap {
  background: var(--card); border-radius: 10px; padding: 14px 16px;
  box-shadow: 0 0 0 1px rgba(0,0,0,.04), 0 1px 3px rgba(0,0,0,.10), 0 2px 6px rgba(0,0,0,.05);
}
/* Calendar rendered as a card inside the home grid (so it sizes/orders like
   a chart). It grows to fit the calendar rather than the chart min-height. */
.home-cal-card { min-height: 0; }
.home-cal-card .dw-body { min-height: 0; }

/* Per-card ⋯ menu on the home page — set size (1..12) + order */
#home-grid .dash-widget { position: relative; }
/* Home charts are read-only — the title isn't an edit affordance here */
#home-grid .dw-title { cursor: default; }
#home-grid .dw-title:hover { color: var(--ink); }
/* …except a linked title (home.js adds .dw-title-link): it IS a link to the
   board view, so keep the pointer + turn the system primary colour on hover.
   Scoped under #home-grid and placed AFTER the rule above so it wins. */
#home-grid .dw-title-link { cursor: pointer; }
#home-grid .dw-title-link:hover { color: var(--primary); }
#home-grid .dw-title-link:hover .dw-open-ico { opacity: 1; }
.home-card-menu {
  position: absolute; top: 8px; right: 10px; z-index: 4;
  width: 24px; height: 24px; padding: 0; border: none; border-radius: 6px;
  background: rgba(255,255,255,.72); color: var(--ink-soft);
  font-size: 16px; line-height: 1; cursor: pointer; opacity: .55;
  display: inline-flex; align-items: center; justify-content: center;
  transition: opacity .1s, background .1s;
}
.home-card-menu:hover { opacity: 1; background: var(--surface); }
.home-card-pop {
  position: fixed; z-index: 1200; min-width: 210px;
  background: var(--card); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 8px 28px rgba(0,0,0,.18); padding: 10px;
  display: flex; flex-direction: column; gap: 9px;
}
.home-card-pop .hcp-row { display: flex; align-items: center; gap: 8px; }
.home-card-pop .hcp-label { min-width: 46px; font-size: 12.5px; font-weight: 600; color: var(--ink-soft); }
.home-card-pop input[type="range"].hcp-size { flex: 1; min-width: 90px; }
.home-card-pop .hcp-size-val { min-width: 22px; text-align: right; font-size: 13px; font-weight: 700; color: var(--ink); }
.home-card-pop select.hcp-pos {
  flex: 1; padding: 5px 8px; border: 1px solid var(--line); border-radius: 6px;
  background: var(--card); color: var(--ink); font-size: 13px;
}
.home-card-pop .hcp-actions { justify-content: flex-end; gap: 8px; margin-top: 1px; }

/* Table widget — compact aggregate table inside a dashboard card */
.dw-table-wrap { overflow: auto; max-height: 320px; }
.dw-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.dw-table thead th {
  background: var(--surface); color: var(--ink-soft);
  font-weight: 600; font-size: 11.5px; text-transform: uppercase;
  padding: 7px 10px; border-bottom: 2px solid var(--line);
  text-align: left; position: sticky; top: 0; z-index: 1;
}
.dw-table thead th.dw-table-num { text-align: right; }
.dw-table tbody td, .dw-table tbody th {
  padding: 6px 10px; border-bottom: 1px solid var(--line);
  font-variant-numeric: tabular-nums;
}
.dw-table tbody tr:hover td, .dw-table tbody tr:hover th { background: #f8fbff; }
.dw-table tbody tr:last-child td, .dw-table tbody tr:last-child th { border-bottom: none; }
.dw-table-cat { display: flex; align-items: center; gap: 8px; }
.dw-table-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; }
.dw-table-num { text-align: right; font-weight: 600; }
.dw-table-rowhead { font-weight: 600; color: var(--ink); }
@media (max-width: 1100px) {
  .dash-widget, .dash-widget.dw-bar, .dash-widget.dw-line,
  .dash-widget.dw-donut, .dash-widget.dw-pie { grid-column: span 12; }
  .dash-widget.dw-kpi { grid-column: span 6; }
}
.modal-wide { max-width: 480px; width: 92vw; }

/* Item detail modal — single card with clear border + structure.
   Coloured top stripe = group accent. Body has visible border and rows
   are clearly separated. */
.idet-modal {
  width: 560px; max-width: 96vw;
  padding: 0; overflow: hidden;
  border: 1px solid var(--line);
}
/* Coloured stripe at the very top — group accent */
.idet-modal::before {
  content: ''; display: block; height: 4px;
  background: var(--idet-accent, var(--primary));
}
.idet-head {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 16px 22px 14px;
  border-bottom: 1px solid var(--line);
  background: #fafbfc;
}
.idet-head-main { flex: 1; min-width: 0; }
.idet-name {
  margin: 0; font-size: 18px; word-break: break-word; line-height: 1.4;
  font-weight: 700; color: var(--ink);
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
/* Source board name — a neutral chip shown before the group chip. */
.idet-board {
  font-size: 11px; font-weight: 700; letter-spacing: .02em;
  color: var(--ink-soft); background: var(--bg-soft, rgba(0,0,0,.06));
  border: 1px solid var(--line); padding: 3px 10px; border-radius: 11px;
  white-space: nowrap;
}
.idet-group {
  font-size: 11px; font-weight: 700; letter-spacing: .03em;
  text-transform: uppercase; color: #fff;
  padding: 3px 10px; border-radius: 11px;
  white-space: nowrap;
}
.idet-close-x {
  width: 28px; height: 28px; border: none; background: transparent;
  font-size: 22px; line-height: 1; cursor: pointer; color: var(--ink-soft);
  border-radius: 6px; display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.idet-close-x:hover { background: rgba(0,0,0,.06); color: var(--ink); }

.idet-body {
  /* 2 fields per row; long_text rows span both columns. */
  display: grid; grid-template-columns: 1fr 1fr; gap: 2px 24px;
  padding: 8px 22px 14px;
  max-height: 60vh; overflow-y: auto;
  background: var(--card);
}
/* Each row is a single line: "icon Title: value" */
.idet-row { padding: 5px 0; line-height: 1.5; min-width: 0; word-break: break-word; }
.idet-title {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12.5px; color: var(--ink-soft); font-weight: 600;
}
.idet-title::after { content: ':'; margin-left: 1px; }
.idet-icon { display: inline-flex; color: var(--ink-soft); flex-shrink: 0; }
.idet-val {
  display: inline; margin-left: 6px;
  font-size: 13px; color: var(--ink);
}
/* Wide rows (long_text) span both grid columns + value moves to its own line */
.idet-row-wide { grid-column: 1 / -1; padding: 8px 0; }
.idet-row-wide .idet-title::after { content: ''; }
.idet-row-wide .idet-val { display: block; margin: 4px 0 0; }
.idet-footer {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 22px; background: #fafbfc;
  border-top: 1px solid var(--line);
}
.idet-foot-left {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  flex: 1; min-width: 0;
}
.idet-foot-people {
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--ink-soft); font-size: 12px;
}
.idet-foot-people-icon { display: inline-flex; color: var(--ink-soft); }
.idet-foot-people-val { display: inline-flex; flex-wrap: wrap; gap: 4px; }
/* Inline cell renderers behave well inside .idet-val.
   All multi-value containers go inline so their chips flow with the title
   text and wrap naturally if they overflow the line. */
.idet-val .cv-status, .idet-val .cv-status-inline,
.idet-val .cv-tag, .idet-val .cv-avatar,
.idet-val .cv-chip, .idet-val .cv-link,
.idet-val .cv-num, .idet-val .cv-rating, .idet-val .cv-date,
.idet-val .cv-check, .idet-val .cv-formula, .idet-val .cv-text {
  display: inline-block; vertical-align: middle;
}
.idet-val .cv-people, .idet-val .cv-people-full { display: inline; }
.idet-val .cv-people > *, .idet-val .cv-people-full > * { margin: 0 2px 2px 0; }
/* Files only appear inside .idet-row-wide — keep their vertical-list layout */
.idet-val .cv-files { display: flex; flex-direction: column; gap: 4px; margin-top: 2px; }

/* Detail-modal-only renderers — fuller info than the cell-compact versions */
.cv-longtext {
  white-space: pre-wrap; line-height: 1.55; font-size: 13px;
}
.cv-status-inline {
  display: inline-block; color: #fff; padding: 3px 10px;
  border-radius: 11px; font-size: 11.5px; font-weight: 600; line-height: 1.4;
}
.cv-people-full { display: inline-flex; flex-wrap: wrap; gap: 4px; }
.cv-person-chip {
  display: inline-flex; align-items: center; gap: 6px;
  background: #eef5ff; color: var(--ink);
  padding: 2px 10px 2px 2px; border-radius: 12px;
  font-size: 12px; font-weight: 500;
}
.cv-person-chip .cv-avatar {
  width: 20px; height: 20px; font-size: 10px;
  background: var(--primary); color: #fff;
  border-radius: 50%; display: inline-flex;
  align-items: center; justify-content: center; font-weight: 700;
}
.dw-size-range { width: 100%; accent-color: var(--primary); margin-top: 4px; }
.dw-size-val { float: right; font-weight: 600; color: var(--primary); font-variant-numeric: tabular-nums; }
.dw-axis-field { display: flex; flex-direction: column; gap: 6px; margin: 10px 0 14px; padding: 10px 12px; background: var(--surface); border-radius: 6px; }
.dw-check { display: flex; align-items: center; gap: 8px; font-size: 13px; cursor: pointer; }
.dw-check input[type="checkbox"] { accent-color: var(--primary); width: 16px; height: 16px; cursor: pointer; }
/* Custom icon-capable dropdown (used by the chart editor) */
.csel { position: relative; display: block; margin-top: 4px; }
.csel-btn {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 7px 10px; border: 1px solid var(--line); border-radius: 6px;
  background: var(--card); font-size: 13px; color: var(--ink); font-family: inherit;
  text-align: left; cursor: pointer; transition: border-color .12s ease;
}
.csel-btn:hover { border-color: var(--primary); }
.csel.csel-open .csel-btn { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(87,155,252,.15); }
.csel-ico { display: inline-flex; align-items: center; justify-content: center; width: 16px; color: var(--ink-soft); }
.csel-lbl { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.csel-caret { color: var(--ink-soft); font-size: 11px; }
.csel-pop {
  position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: 100;
  max-height: 280px; overflow-y: auto;
  background: var(--card); border: 1px solid var(--line); border-radius: 6px;
  box-shadow: 0 6px 24px rgba(0,0,0,.12); padding: 4px;
}
.csel-opt {
  display: flex; align-items: center; gap: 8px; padding: 7px 10px;
  border-radius: 4px; cursor: pointer; font-size: 13px;
}
.csel-opt:hover { background: var(--surface); }
.csel-opt.selected { background: #eef5ff; color: var(--primary); font-weight: 600; }
.csel-opt .csel-ico { color: inherit; }

.thm-preview {
  display: flex; gap: 3px; margin-top: 6px; min-height: 18px; align-items: center;
}
.thm-sw {
  display: inline-block; width: 18px; height: 18px; border-radius: 3px;
  border: 1px solid rgba(0,0,0,.05);
}
.thm-auto { color: var(--ink-soft); font-size: 11.5px; font-style: italic; }

/* Activity log viewer page */
.logs-wrap { padding: 0 32px 40px; }
.logs-filters {
  display: flex; flex-wrap: wrap; gap: 10px 14px; align-items: flex-end;
  background: var(--card); border: 1px solid var(--line); border-radius: 8px;
  padding: 12px 14px; margin-bottom: 12px;
}
.logs-filters label {
  display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--ink-soft);
  min-width: 150px;
}
.logs-filters select, .logs-filters input[type="date"] {
  height: 30px; padding: 0 8px;
  border: 1px solid var(--line); border-radius: 6px; background: var(--card);
  font-family: inherit; font-size: 13px;
}
/* Wide table → scroll horizontally INSIDE this box instead of pushing the
   whole page past the screen edge. */
.logs-tablewrap {
  max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch;
  border-radius: 8px;
}
.logs-table {
  width: 100%; border-collapse: collapse; background: var(--card);
  border: 1px solid var(--line); border-radius: 8px; overflow: hidden;
  font-size: 13px;
}
.logs-table th, .logs-table td {
  padding: 8px 12px; text-align: left; vertical-align: top;
  border-bottom: 1px solid var(--line);
}
.logs-table th {
  background: var(--bg); font-size: 11.5px; font-weight: 600;
  color: var(--ink-soft); text-transform: uppercase; letter-spacing: .02em;
}
.logs-table tbody tr:last-child td { border-bottom: 0; }
.logs-table tbody tr:hover { background: var(--surface); }
.logs-action {
  display: inline-block; padding: 2px 8px; border-radius: 11px;
  background: rgba(0,115,234,.08); color: var(--primary);
  font-size: 11.5px; font-weight: 600;
}
.logs-action-login,
.logs-action-logout      { background: rgba(0,200,117,.1);  color: #00a35e; }
.logs-action-login_failed,
.logs-action-delete_board,
.logs-action-delete_folder,
.logs-action-delete_view,
.logs-action-delete_column,
.logs-action-delete_item,
.logs-action-delete_user { background: rgba(226,68,92,.12); color: #b8253a; }
.logs-pager {
  display: flex; align-items: center; gap: 12px; justify-content: center;
  margin-top: 14px;
}

/* Create-board template picker */
.tpl-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 18px; padding: 22px 32px;
}

/* Import-preview modal — wider than cs-set-modal default */
.import-modal { width: 940px; max-width: 96vw; }
.import-modal h3 { margin: 0 0 16px; }
.import-modal .im-folder-hint {
  margin-top: 8px; font-size: 12px; color: var(--ink-soft);
}
.import-modal .im-col-wrap {
  max-height: 280px; overflow: auto;
  border: 1px solid var(--line); border-radius: 6px; background: var(--card);
}
.import-modal .im-col-table {
  width: 100%; border-collapse: separate; border-spacing: 0; font-size: 12.5px;
}
.import-modal .im-col-table thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--bg); padding: 8px 10px;
  border-bottom: 1px solid var(--line);
  font-size: 11.5px; color: var(--ink-soft); font-weight: 600; text-align: left;
}
.import-modal .im-col-table tbody td {
  padding: 6px 10px; border-bottom: 1px solid var(--line);
  vertical-align: middle;
}
.import-modal .im-col-table tbody tr:last-child td { border-bottom: 0; }
.import-modal .im-col-incl { width: 32px; text-align: center; }
.import-modal .im-col-incl input { width: 14px; height: 14px; margin: 0; accent-color: var(--primary); }
.import-modal .im-col-name input,
.import-modal .im-col-type select {
  width: 100%; padding: 5px 8px; margin: 0;
  border: 1px solid var(--line); border-radius: 5px;
  font-size: 12.5px; background: var(--card); color: var(--ink);
}
.import-modal .im-col-name input:focus,
.import-modal .im-col-type select:focus { outline: 2px solid var(--primary); outline-offset: -1px; }
.import-modal tr.is-excluded {
  background: var(--bg);
}
.import-modal tr.is-excluded td { opacity: .45; }
.import-modal .im-sample-chip {
  display: inline-block; padding: 1px 7px; margin: 1px 3px 1px 0;
  background: var(--bg); border-radius: 10px; font-size: 11px; color: var(--ink-soft);
  max-width: 120px; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; vertical-align: middle;
}
.import-modal .im-cand-badge {
  display: inline-block; padding: 1px 6px; margin-left: 4px;
  background: rgba(0,115,234,.1); color: var(--primary);
  border-radius: 8px; font-size: 10.5px; font-weight: 600;
  vertical-align: middle;
}
.import-modal .im-sample-wrap {
  max-height: 220px; overflow: auto;
  border: 1px solid var(--line); border-radius: 6px; background: var(--card);
}
.import-modal .im-sample-table {
  width: 100%; border-collapse: separate; border-spacing: 0; font-size: 12px;
}
.import-modal .im-sample-table thead th {
  position: sticky; top: 0;
  background: var(--bg); padding: 6px 10px;
  border-bottom: 1px solid var(--line);
  font-size: 11.5px; color: var(--ink-soft); font-weight: 600; text-align: left;
  white-space: nowrap;
}
.import-modal .im-sample-table tbody td {
  padding: 5px 10px; border-bottom: 1px solid var(--line);
  white-space: nowrap; max-width: 220px; overflow: hidden; text-overflow: ellipsis;
}
.import-modal .im-sample-table tbody tr:last-child td { border-bottom: 0; }
/* optgroup label inside the group-source <select> */
.import-modal .cs-select optgroup { font-style: normal; font-weight: 600; }

.tpl-card {
  background: var(--card); border: 1px solid var(--line); border-radius: 12px;
  padding: 20px; display: flex; flex-direction: column; transition: .12s;
}
.tpl-card:hover { border-color: var(--primary); box-shadow: 0 6px 22px rgba(0,115,234,.12); }
.tpl-icon {
  width: 52px; height: 52px; border-radius: 12px; background: var(--surface);
  display: flex; align-items: center; justify-content: center; margin-bottom: 12px;
}
.tpl-name { font-size: 16px; font-weight: 700; }
.tpl-desc { color: var(--ink-soft); font-size: 13px; margin: 4px 0 8px; flex: 1; }
.tpl-meta { color: var(--ink-soft); font-size: 12px; margin-bottom: 14px; }
.tpl-btn { justify-content: center; }

/* navbar user menu */
.nav-user-btn {
  display: flex; align-items: center; gap: 9px; background: transparent;
  border: none; color: #fff; padding: 4px 8px; border-radius: 7px;
}
.nav-user-btn:hover { background: rgba(255,255,255,.08); }
.num-caret { display: inline-flex; color: #8b8fb0; }

/* btn-danger */
.btn-danger { color: var(--red); border-color: #f3c2c8; }
.btn-danger:hover { background: #fdeaec; }
.btn:disabled { opacity: .45; cursor: not-allowed; }

/* manage-users modal */
.modal.modal-lg { width: 580px; }
.user-list {
  display: flex; flex-direction: column; gap: 8px;
  max-height: 340px; overflow-y: auto; margin-bottom: 16px;
}
.user-row { display: flex; align-items: center; gap: 8px; }
.user-row.user-off { opacity: .5; }
.u-name {
  flex: 1; min-width: 0; padding: 7px 9px; font-size: 13px;
  border: 1px solid var(--line-dark); border-radius: 6px; font-family: inherit;
}
.u-email {
  width: 150px; flex-shrink: 0; font-size: 12px; color: var(--ink-soft);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.u-role {
  padding: 6px 8px; font-size: 12px; border: 1px solid var(--line-dark);
  border-radius: 6px; font-family: inherit;
}
.user-add { border-top: 1px solid var(--line); padding-top: 16px; }
.ua-title { font-weight: 700; font-size: 14px; margin-bottom: 12px; }
.ua-field { display: block; font-size: 12px; font-weight: 600; color: var(--ink-soft); margin-bottom: 11px; }
.ua-field input, .ua-field select {
  display: block; width: 100%; margin-top: 5px; padding: 7px 10px; font-size: 13px;
  border: 1px solid var(--line-dark); border-radius: 7px; font-family: inherit; font-weight: 400;
  color: var(--ink);
}
.ua-field input:focus, .ua-field select:focus { outline: none; border-color: var(--primary); }
.ua-row2 { display: flex; gap: 10px; }
.ua-row2 .ua-field { flex: 1; }
.ua-submit { width: 100%; justify-content: center; margin-top: 4px; }

/* data-management pages (users.php etc.) */
.data-page { padding: 8px 32px 50px; }
.card { background: var(--card); border: 1px solid var(--line); border-radius: 12px; padding: 20px; margin-bottom: 20px; }
.card-title { font-size: 15px; font-weight: 700; margin-bottom: 14px; }
.ulist { display: flex; flex-direction: column; gap: 8px; }
.ulist .user-row { flex-wrap: wrap; }
.u-badge { font-size: 11px; font-weight: 700; padding: 3px 9px; border-radius: 11px; flex-shrink: 0; }
.u-badge.on { background: #e6f8f0; color: #027a48; }
.u-badge.off { background: #fdeaec; color: #b42318; }
.u-self { font-size: 12px; color: var(--ink-soft); }
.side-manage { padding-bottom: 10px; }

/* data table (users.php) */
/* Wide 8-column table → scroll inside this box instead of spilling the page
   past the screen edge. min-width keeps the columns readable while scrolling. */
.dtable-wrap { max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }
.dtable { width: 100%; border-collapse: collapse; min-width: 680px; }
.dtable th {
  text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--ink-soft); font-weight: 700; padding: 9px 12px; border-bottom: 2px solid var(--line);
}
.dtable td { padding: 9px 12px; border-bottom: 1px solid var(--line); font-size: 13px; vertical-align: middle; }
.dtable tr:last-child td { border-bottom: none; }
.dtable tbody tr:hover { background: var(--surface); }
.dt-muted { color: var(--ink-soft); }
.dt-act { white-space: nowrap; }
.dt-act .btn { margin-right: 4px; }
.u-inp {
  padding: 6px 9px; border: 1px solid var(--line-dark); border-radius: 6px;
  font-size: 13px; font-family: inherit; width: 100%; max-width: 240px; color: var(--ink);
}
.u-inp:focus { outline: none; border-color: var(--primary); }
.u-inp-sm { max-width: 140px; }

.page-head-row { display: flex; justify-content: space-between; align-items: center; gap: 16px; }

/* user avatars + users.php */
.cv-avatar-img { object-fit: cover; }
.dt-av { width: 44px; }
.inline-form { display: inline; }
.tag {
  display: inline-block; background: var(--surface); border: 1px solid var(--line);
  border-radius: 11px; padding: 2px 9px; font-size: 11px; font-weight: 600;
}
.av-row { display: flex; align-items: center; gap: 14px; margin-bottom: 14px; }
.av-preview {
  width: 64px; height: 64px; border-radius: 50%; flex-shrink: 0; overflow: hidden;
  background: var(--primary); color: #fff; font-size: 22px; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
}
.av-preview img { width: 100%; height: 100%; object-fit: cover; }
.av-upload { flex: 1; margin-bottom: 0; }
.av-upload input[type=file] { font-size: 12px; margin-top: 5px; }
.av-upload small { display: block; color: var(--ink-soft); font-weight: 400; margin-top: 4px; font-size: 11px; }
.ua-field textarea {
  display: block; width: 100%; margin-top: 5px; padding: 7px 10px; font-size: 13px;
  border: 1px solid var(--line-dark); border-radius: 7px; font-family: inherit;
  font-weight: 400; resize: vertical; color: var(--ink);
}
.ua-field textarea:focus { outline: none; border-color: var(--primary); }

/* คำนวนเวลา (date_calc) settings modal — source select + offset grid */
.dc-off-label { font-weight: 600; font-size: 12px; color: var(--ink-soft); margin: 12px 0 6px; }
.dc-off-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }
.dc-off { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--ink-soft); font-weight: 600; }
.dc-off .dc-num { width: 100%; text-align: center; padding: 6px 4px; font-size: 13px; font-weight: 400;
  border: 1px solid var(--line-dark); border-radius: 7px; color: var(--ink); font-family: inherit; }
.dc-off .dc-num:focus { outline: none; border-color: var(--primary); }
.dc-nosrc { font-size: 11.5px; margin-top: 6px; color: var(--danger); }
@media (max-width: 560px) { .dc-off-grid { grid-template-columns: repeat(3, 1fr); } }

.g-menu {
  position: absolute; left: -22px; top: 0; bottom: 0;
  opacity: 0; cursor: pointer; display: flex; align-items: center; color: var(--ink-soft);
}
.group-head:hover .g-menu { opacity: 1; }
.g-menu:hover { color: var(--ink); }

/* subitems (tasks) */
.row-expand {
  display: inline-flex; cursor: pointer; color: var(--ink-soft);
  transform: rotate(-90deg); transition: transform .15s ease; flex-shrink: 0;
}
.row-expand.expanded { transform: rotate(0); }
.row-expand:hover { color: var(--ink); }
.sub-count {
  font-size: 10.5px; font-weight: 700; background: var(--surface); color: var(--ink-soft);
  border-radius: 9px; padding: 1px 7px; flex-shrink: 0;
}
.subitem-row td { background: #fbfbfd; }
.subitem-row:hover td { background: #f3f4f8; }
.cell-subname { font-weight: 400; }
.sub-twig {
  width: 14px; height: 9px; flex-shrink: 0; margin-left: 8px;
  border-left: 2px solid var(--line-dark); border-bottom: 2px solid var(--line-dark);
  border-bottom-left-radius: 5px; align-self: flex-start; margin-top: 2px;
}
.subadd-row td { background: #fbfbfd; }
.add-subitem-input {
  border: none; background: transparent; font-size: 12.5px; width: 100%;
  padding: 0 0 0 24px; color: var(--ink-soft);
}
.add-subitem-input:focus { outline: none; }

/* nested subitem table */
.sub-cell { padding: 0 !important; background: #fafbfd; border-right: none !important; }
.sub-wrap { padding: 4px 4px 6px 32px; }
.subtbl {
  border-collapse: separate; border-spacing: 0; width: 100%;
  background: var(--card); border: 1px solid var(--line); border-radius: 8px; overflow: visible;
}
.subtbl th, .subtbl td {
  border-bottom: 1px solid var(--line); border-right: 1px solid var(--line);
  height: 34px; font-size: 12.5px;
}
.subtbl thead th {
  background: #fafbfd; color: var(--ink-soft); font-weight: 600; font-size: 11.5px;
  padding: 0 10px; text-align: left; white-space: nowrap; position: relative;
}
.subtbl thead th[data-col] { padding-right: 28px; }
.subtbl tbody tr:last-child td { border-bottom: none; }
.subtbl tbody tr:hover td { background: #f8f9fc; }
.g-bar-sub { width: 8px; padding: 0 !important; position: relative; background: transparent !important; }
.g-bar-sub .row-menu { left: -22px; right: auto; }
.sub-name-head { min-width: 220px; }
.cell-subname { font-weight: 500; }
.subtbl .subadd-row td { border-bottom: none; background: #fafbfd; }
.add-subitem-input {
  border: none; background: transparent; font-size: 12.5px; width: 100%;
  padding: 0 4px; color: var(--ink-soft);
}
.add-subitem-input:focus { outline: none; }

/* file column */
.cv-files { display: flex; flex-wrap: wrap; gap: 4px; }
.cv-file {
  display: inline-flex; align-items: center;
  background: #e7f1ff; border-radius: 11px; font-size: 11.5px; font-weight: 600;
  max-width: 200px;
}
.cv-file:hover { background: #d6e7ff; }
.cv-file-link {
  display: inline-flex; align-items: center; gap: 4px;
  color: var(--primary); padding: 2px 4px 2px 8px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-decoration: none;
}
.cv-file-x {
  border: none; background: #f5a3ad; color: #fff;
  width: 16px; height: 16px; margin: 0 4px 0 2px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 700; line-height: 1; cursor: pointer;
  border-radius: 50%; box-shadow: 0 1px 2px rgba(0,0,0,.1);
  transition: transform .12s ease, background .12s ease;
}
.cv-file-x:hover { background: #e2445c; transform: scale(1.15); }
.cv-file-add { color: var(--ink-soft); font-size: 12px; }

/* formula column */
.cv-formula { font-variant-numeric: tabular-nums; font-weight: 600; }
.cv-formula.err { color: var(--red); }
.formula-refs {
  font-size: 11.5px; color: var(--ink-soft); background: var(--surface);
  padding: 8px 10px; border-radius: 6px; margin: 8px 0 12px;
  max-height: 160px; overflow-y: auto;
}
.formula-refs b { color: var(--ink); display: block; margin-bottom: 6px; }
.formula-ref-list { display: flex; flex-wrap: wrap; gap: 6px; }
.formula-ref-btn {
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid var(--line); background: var(--card); border-radius: 6px;
  padding: 4px 8px; font-size: 12px; cursor: pointer;
  transition: all .12s ease;
}
.formula-ref-btn:hover { border-color: var(--primary); background: #eef5ff; }
/* Swap colour roles so the column title reads as the prominent label
   (primary blue) and the {col_N} slug is the secondary muted bit. */
.formula-ref-slug {
  font-family: ui-monospace, Menlo, Consolas, monospace;
  color: var(--ink-soft); font-weight: 600;
}
.formula-ref-title { color: var(--primary); font-weight: 600; }
.formula-ta {
  width: 100%; box-sizing: border-box;
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 13px; line-height: 1.5;
  padding: 8px 10px; border: 1px solid var(--line); border-radius: 6px;
  background: var(--card); color: var(--ink); resize: vertical;
  min-height: 64px;
}
.formula-ta:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px rgba(87,155,252,.18); }
.formula-ref-group { margin-top: 8px; }
.formula-ref-grp-title {
  display: block; font-size: 11.5px; font-weight: 600; color: var(--primary);
  margin: 4px 0 4px;
}
.formula-ref-board { margin: 4px 0 6px 8px; padding-left: 8px; border-left: 2px solid var(--line); }
.formula-ref-board-name {
  display: block; font-size: 11px; color: var(--ink-soft);
  margin-bottom: 4px;
}
.formula-ops {
  display: flex; flex-wrap: wrap; gap: 4px; margin: 6px 0 4px;
}
.formula-op-btn {
  border: 1px solid var(--line); background: var(--card);
  width: 30px; height: 30px; border-radius: 6px;
  font-size: 15px; font-weight: 700; color: var(--ink);
  cursor: pointer; transition: all .12s ease;
}
.formula-op-btn:hover { border-color: var(--primary); background: #eef5ff; color: var(--primary); }

/* ═══════════════════════════════════════════════════════════════════════
   Responsive layout — tablet + mobile.
   Breakpoints
     ≤ 1024px : tablet (sidebar still visible but tighter, dashboard 2-col)
     ≤ 768px  : mobile portrait — sidebar slides in as an overlay drawer,
                page padding collapses, modals go near-fullscreen.
     ≤ 480px  : very narrow phones — single-column everywhere, tighter UI.
   The desktop styles above are the canonical state; everything below
   adjusts incrementally.
   ═══════════════════════════════════════════════════════════════════════ */

/* ----- Tablet ----- */
@media (max-width: 1024px) {
  .sidebar { width: 200px; }
  .page-head, .board-actions, .board-top, .folder-group { padding-left: 18px; padding-right: 18px; }
  .board-grid { padding: 18px; gap: 14px;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  }
  .nav-user-name { display: none; }     /* save horizontal room */
  /* Dashboard widgets — fall back to 2-wide instead of 1-wide so the
     screen still feels usable. .dw-kpi stays narrower. */
  .dash-widget               { grid-column: span 6; }
  .dash-widget.dw-kpi        { grid-column: span 3; }
}

/* ----- Mobile portrait ----- */
@media (max-width: 768px) {
  /* Off-canvas sidebar drawer. The body class .mobile-nav-open (set by
     app.js when #nav-toggle is clicked) slides it in. A semi-opaque
     backdrop fills the rest of the viewport so tapping outside closes
     the drawer. */
  .sidebar {
    position: fixed; top: 54px; left: 0; bottom: 0;
    width: 260px; max-width: 80vw; z-index: 45;
    transform: translateX(-100%); transition: transform .22s ease;
    box-shadow: 4px 0 24px rgba(0,0,0,.18);
  }
  body.mobile-nav-open .sidebar { transform: translateX(0); }
  body.mobile-nav-open::after {
    content: ""; position: fixed; inset: 54px 0 0 0;
    background: rgba(0,0,0,.4); z-index: 40;
  }
  /* The collapsed-rail state is meaningless on mobile — drawer either
     shown in full or hidden entirely. */
  .app.nav-collapsed .sidebar { width: 260px; transform: translateX(-100%); }
  .app.nav-collapsed .side-label,
  .app.nav-collapsed .side-section span,
  .app.nav-collapsed .side-folder-head { display: revert; }

  /* Navbar — compact */
  .navbar { height: 54px; padding: 0 10px; gap: 8px; }
  .nav-brand { font-size: 14px; }
  .nav-user .render-avatar, .nav-user .avatar { width: 28px; height: 28px; }

  /* Page chrome — squeeze horizontal padding down so the content area
     uses nearly every pixel. Vertical padding stays comfortable. */
  .page-head           { padding: 18px 8px 4px; }
  .page-head h1        { font-size: 20px; }
  .board-top           { padding: 14px 8px 0; }
  .board-actions       { padding: 12px 8px; flex-wrap: wrap; gap: 6px; }
  .folder-group        { padding: 0 8px; }
  .folder-group-body .board-grid { padding: 10px 0 2px; }
  .alert               { margin-left: 8px; margin-right: 8px; }

  /* Board grid → 1 column, tight side padding */
  .board-grid {
    padding: 12px 8px; gap: 12px;
    grid-template-columns: 1fr;
  }

  /* View container — table/kanban/calendar/gantt/dashboard/gallery/form
     all wrap in .board-body. Desktop pads 32px each side; on mobile we
     drop that to 8px so the view chrome doesn't waste horizontal pixels.
     Bottom keeps a comfortable scroll cushion. */
  .board-body { padding: 4px 8px 40px; }
  /* Text-editor paper is a fixed real-mm width (A4 = 210mm ≈ 794px) that
     overflows a phone screen. Confine the horizontal scroll to the sheet area
     so the paper stays inside the viewport and scrolls, instead of pushing the
     whole page (and toolbar) off the screen edge. The toolbar + pagebar are
     siblings outside this scroller, so they stay pinned to the viewport width. */
  .te-sheet-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .te-editor { margin: 12px auto; }
  .form-view  { padding: 18px 14px; }
  .form-grid  { gap: 10px; }

  /* Board title — smaller font, wrap if needed */
  .board-title { flex-wrap: wrap; gap: 6px; }
  .board-title h1 { font-size: 18px; }

  /* View tabs already scroll-x; tighten padding */
  .view-tabs { padding-bottom: 0; }

  /* Dashboard widgets → every chart goes full-width (span 12), KPI keeps
     its saved size so a 1- or 2-up of small number tiles still works on
     mobile. The :not(.dw-kpi) selector with !important overrides the
     widget's inline style="grid-column: span N" from dashboard.php. */
  .dash-widget:not(.dw-kpi) { grid-column: span 12 !important; }
  /* The redesigned KPI card lays the icon chip + label + number out
     horizontally, so it needs more room than the old number-only tile —
     2-up (span 6) keeps it readable on phones. */
  .dash-widget.dw-kpi { grid-column: span 6 !important; }
  /* 2-up KPI tiles are narrow on phones — scale the icon chip + number down
     so the value fits without truncation. */
  .dash-widget.dw-kpi { gap: 8px; }
  .dash-widget.dw-kpi .dw-kpi-ico { width: 42px; height: 42px; border-radius: 11px; }
  .dash-widget.dw-kpi .dw-kpi-ico .icon { width: 20px; height: 20px; }
  .dash-widget.dw-kpi .dw-kpi-num { font-size: 15px; }

  /* Modals — near-fullscreen with reduced inner padding */
  .modal { width: 96vw !important; max-width: 96vw !important; padding: 16px !important; }
  .modal h3 { font-size: 17px; }
  .modal-bg { padding: 8px; }

  /* Tables already scroll horizontally; keep min cell width sensible */
  .cell, .cell.cell-name { max-width: 240px; }

  /* Filter bar — wrap rows; trim side padding AND outer margin so the
     bar hugs the content edge. Inner column gap also shrinks. */
  .filter-bar {
    margin: 8px 8px 0 !important;
    padding: 8px !important;
    gap: 8px !important;
  }
  .filter-bar form  { flex-wrap: wrap; gap: 6px; }
  .filter-field     { flex: 1 1 140px; }

  /* Touch-friendly button targets — bump btn-sm a touch */
  .btn { padding: 10px 14px; font-size: 13px; }
  .btn-sm { padding: 8px 12px; font-size: 12px; }

  /* Logs page — trim side padding and keep the table from spilling past the
     screen (it scrolls inside .logs-tablewrap instead). Filter controls go
     full-width so the long select labels don't overflow either. */
  .logs-wrap { padding: 0 12px 32px; }
  .logs-table { font-size: 12px; }
  .logs-filters { flex-wrap: wrap; }
  .logs-filters label { min-width: 0; flex: 1 1 140px; }
  .logs-filters select, .logs-filters input[type="date"] { width: 100%; }

  /* Users page — trim side padding; rows become compact profile bars. */
  .data-page { padding: 8px 12px 40px; }
  .data-page .card { padding: 0; background: transparent; border: none; }

  /* Users table → one compact card bar per user (no horizontal scroll). */
  .dtable-wrap { overflow-x: visible; }
  .dtable { min-width: 0; }
  .dtable thead { display: none; }
  .dtable, .dtable tbody { display: block; }
  .dtable tr {
    display: flex; flex-wrap: wrap; align-items: center; gap: 3px 9px;
    background: var(--card); border: 1px solid var(--line); border-radius: 12px;
    padding: 11px 13px; margin-bottom: 10px;
    box-shadow: 0 1px 3px rgba(0,0,0,.05);
  }
  .dtable td { display: block; border: none; padding: 0; }
  /* ── header row — avatar + name + status badge ── */
  .dtable td.dt-av { order: 1; flex-shrink: 0; }
  .dtable td.dt-av .cv-avatar { width: 34px !important; height: 34px !important; font-size: 12px !important; }
  .dtable td.dt-name { order: 2; flex: 1 1 auto; min-width: 0; font-size: 14px; font-weight: 700;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .dtable td[data-label="สถานะ"] { order: 3; }
  .dtable td[data-label="สถานะ"]::before { display: none; }
  /* divider under the header, then the body (email + meta chips) */
  .dtable td.dt-email {
    order: 4; flex-basis: 100%; font-size: 12px;
    margin-top: 8px; padding-top: 9px; border-top: 1px solid var(--line);
  }
  .dtable td[data-label="เบอร์โทร"],
  .dtable td[data-label="แผนก"],
  .dtable td[data-label="บทบาท"] {
    order: 5; font-size: 11.5px; color: var(--ink);
    display: inline-flex; align-items: center; gap: 4px;
  }
  .dtable td[data-label="เบอร์โทร"]::before,
  .dtable td[data-label="แผนก"]::before,
  .dtable td[data-label="บทบาท"]::before {
    content: attr(data-label) ":"; color: var(--ink-soft); font-weight: 600;
  }
  /* ── footer — divider above the compact, right-aligned action buttons ── */
  .dtable td.dt-act {
    order: 6; flex-basis: 100%; display: flex; justify-content: flex-end; gap: 6px;
    margin-top: 10px; padding-top: 9px; border-top: 1px solid var(--line);
  }
  .dtable td.dt-act .btn { margin-right: 0; }

  /* Login card — pad less on phones */
  .login-card { padding: 28px 22px; }

  /* Form views — already 1-col under 640, mirror here for symmetry */
  .form-view { grid-template-columns: 1fr; }
  .form-grid > .form-view { grid-column: 1 / -1 !important; }

  /* Item-detail modal: stack columns */
  .idet-body { grid-template-columns: 1fr !important; }

  /* iOS auto-zoom defence — Safari pinches in on any form element with a
     computed font-size below 16px on focus. Bumping every text/number/
     date/select/textarea to 16px on mobile suppresses that behaviour
     while padding/height continue to control visual size. */
  input, select, textarea,
  input[type="text"], input[type="email"], input[type="password"],
  input[type="number"], input[type="date"], input[type="search"],
  input[type="tel"], input[type="url"],
  .fr-input, .add-item-input, .cs-select, .cs-input {
    font-size: 16px !important;
  }
  /* Skip 300ms wait + double-tap-zoom on everything the user actually
     interacts with. */
  button, a, .btn, .opt, .cell, .filter-field, .view-tab,
  .kanban-card, .side-link, .sb-menu, .col-menu, .g-menu, .dw-menu {
    touch-action: manipulation;
  }

  /* iOS <input type="date"> quirks. Mobile Safari renders date fields
     differently from text inputs:
       1. Default `-webkit-appearance: textfield` keeps an empty field
          collapsed because there's no visible text — force min-height.
       2. Center text + left-align (iOS pushes it right by default).
       3. Show a pointer cursor so the field feels tappable.
       4. Reset the inner picker indicator's spacing so it doesn't
          overlap the value text. */
  input[type="date"] {
    -webkit-appearance: none;
    appearance: none;
    min-height: 36px;
    width: 100%;
    box-sizing: border-box;
    padding: 6px 10px;
    background: var(--card);
    text-align: left;
    line-height: 1.4;
    cursor: pointer;
  }
  input[type="date"]::-webkit-date-and-time-value { text-align: left; }
  input[type="date"]::-webkit-calendar-picker-indicator {
    opacity: .55; padding: 0; margin-left: 4px;
  }
  input[type="date"]:not(:focus):invalid::-webkit-datetime-edit {
    color: var(--ink-soft);
  }
  /* iOS Safari stacks day/month/year on separate lines because each
     ::-webkit-datetime-edit-*-field defaults to display:block in narrow
     date inputs. Force every part to inline-flex with !important so
     "25/05/2025" stays on one row. The block above is untouched. */
  input[type="date"] {
    height: 36px;
    max-height: 36px;
    overflow: hidden;
    white-space: nowrap;
  }
  input[type="date"]::-webkit-datetime-edit {
    display: inline-flex !important;
    flex-direction: row !important;
    flex-wrap: nowrap !important;
    align-items: center;
    height: 100%;
    padding: 0;
    white-space: nowrap;
    overflow: hidden;
  }
  input[type="date"]::-webkit-datetime-edit-fields-wrapper {
    display: inline-flex !important;
    flex-direction: row !important;
    flex-wrap: nowrap !important;
    white-space: nowrap;
  }
  input[type="date"]::-webkit-datetime-edit-day-field,
  input[type="date"]::-webkit-datetime-edit-month-field,
  input[type="date"]::-webkit-datetime-edit-year-field,
  input[type="date"]::-webkit-datetime-edit-text {
    display: inline-block !important;
    padding: 0;
    white-space: nowrap;
  }
}

/* ----- Very narrow phones ----- */
@media (max-width: 480px) {
  .navbar .nav-brand span:not(.logo) { display: none; }    /* logo only */
  .page-head h1 { font-size: 18px; }
  .board-title h1 { font-size: 16px; }
  .board-actions .btn { width: 100%; justify-content: center; }
  .board-actions > .muted { display: none; }   /* hint text takes too much room */
}

/* Larger touch targets on coarse-pointer (touch) devices regardless of
   width — tablets in landscape land here too. */
@media (pointer: coarse) {
  .opt { padding: 10px 12px; }                 /* popover menu items */
  .sb-menu, .sf-menu, .g-menu, .col-menu, .dw-menu { opacity: 1; }  /* always visible — no hover */
}

/* ---------- Trash (soft delete recovery) — colourful recovery page ----------
   Per-section accent: boards = blue, columns = violet, rows = teal. Cards read
   --tc (solid) / --tc-bg (tint) so one set of rules paints all three. */
.tc-board { --tc: #3b82f6; --tc-bg: rgba(59,130,246,.13); }
.tc-col   { --tc: #8b5cf6; --tc-bg: rgba(139,92,246,.13); }
.tc-row   { --tc: #14b8a6; --tc-bg: rgba(20,184,166,.13); }

/* Sections */
.trash-wrap { display: flex; flex-direction: column; gap: 24px; }
.trash-sec-head {
  display: flex; align-items: center; gap: 9px;
  font-weight: 700; font-size: 14.5px; color: var(--ink); margin-bottom: 11px;
}
.trash-sec-ico {
  width: 28px; height: 28px; border-radius: 8px; flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--tc-bg); color: var(--tc);
}
.trash-count {
  font-size: 12px; font-weight: 700; color: #fff; background: var(--tc);
  border-radius: 999px; padding: 1px 9px; min-width: 22px; text-align: center;
}
.trash-list { display: flex; flex-direction: column; gap: 10px; }

/* Cards */
.trash-row {
  position: relative; display: flex; align-items: center; gap: 13px;
  padding: 12px 16px 12px 18px; border: 1px solid var(--line);
  border-radius: 12px; background: var(--card); overflow: hidden;
  transition: transform .12s ease, box-shadow .12s ease, border-color .12s ease;
  animation: trashIn .25s ease both;
}
.trash-row::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 4px;
  background: var(--tc);
}
.trash-row:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 22px rgba(0,0,0,.09);
  border-color: var(--tc);
}
@keyframes trashIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.trash-ico {
  width: 38px; height: 38px; flex-shrink: 0; border-radius: 10px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--tc-bg); color: var(--tc);
}
.trash-ico .icon { width: 19px; height: 19px; }
.trash-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.trash-name { font-weight: 600; font-size: 14px; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.trash-meta { font-size: 12px; color: var(--ink-soft); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Days-left pill — urgency colours */
.trash-left {
  display: inline-flex; align-items: center; gap: 4px; flex-shrink: 0;
  font-size: 12px; font-weight: 700; border-radius: 999px; padding: 4px 11px; white-space: nowrap;
}
.trash-left .icon { width: 12px; height: 12px; }
.trash-left .tl-num { display: none; }   /* desktop shows the full "เหลือ N วัน" */
.tl-ok   { color: #16a34a; background: rgba(34,197,94,.15); }
.tl-warn { color: #d97706; background: rgba(245,158,11,.17); }
.tl-soon { color: #dc2626; background: rgba(239,68,68,.16); }

/* Actions */
.trash-acts { display: flex; gap: 7px; flex-shrink: 0; }

/* Multi-select mode — header toolbar + per-row checkbox (replaces the icon). */
.trash-toolbar { display: flex; gap: 8px; flex-wrap: wrap; flex-shrink: 0; }
.trash-check { display: none; width: 38px; height: 38px; flex-shrink: 0; align-items: center; justify-content: center; cursor: pointer; }
.trash-check input { width: 18px; height: 18px; cursor: pointer; accent-color: var(--tc); }
body.trash-selecting .trash-check { display: inline-flex; }
body.trash-selecting .trash-ico   { display: none; }
body.trash-selecting .trash-acts  { display: none; }
body.trash-selecting .trash-row   { cursor: pointer; }
.trash-row.sel { border-color: var(--tc); background: var(--tc-bg); }
.trash-bulk-purge:hover:not(:disabled) { color: var(--red); border-color: var(--red); }
.trash-toolbar .btn:disabled { opacity: .5; cursor: default; }

.trash-restore.btn {
  color: #fff; background: var(--tc); border-color: var(--tc);
}
.trash-restore.btn:hover { filter: brightness(1.07); color: #fff; }
.trash-purge:hover { color: var(--red); border-color: var(--red); }

/* Empty state */
.trash-empty { text-align: center; padding: 56px 20px; color: var(--ink-soft); }
.trash-empty-ico {
  width: 78px; height: 78px; margin: 0 auto 14px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: linear-gradient(135deg, rgba(99,102,241,.13), rgba(20,184,166,.13));
  color: #6366f1;
}
.trash-empty-ico .icon { width: 38px; height: 38px; }
.trash-empty p { font-size: 14px; }

@media (max-width: 768px) {
  .trash-row { flex-wrap: wrap; }
  .trash-left { order: 3; }
  .trash-acts { order: 4; margin-left: auto; }
  /* Compact: the days-left pill shows just the number, and the action
     buttons collapse to icon-only squares to save the narrow row. */
  .trash-left .tl-full { display: none; }
  .trash-left .tl-num  { display: inline; }
  .trash-acts .tb-label { display: none; }
  .trash-acts .btn { padding: 7px 9px; gap: 0; }
}
