/* ============================================================
   Admin portal — additions on top of Sneat

   Sneat itself provides the layout, cards, tables, forms and buttons.
   This file only carries what the theme does not:
     1. Thai typography
     2. Semi-dark vertical menu (the free build ships a light menu)
     3. Login screen
     4. Chat components (no Sneat equivalent in the free build)
     5. Calendar / FullCalendar fit-and-finish
   ============================================================ */

/* ---------------------------------------------- 1. typography ---- */
:root {
  --admin-font: 'Public Sans', 'Sarabun', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}

body,
.menu,
.btn,
.form-control,
.form-select,
.table,
.modal,
.dropdown-menu {
  font-family: var(--admin-font) !important;
}

/* Thai glyphs sit taller than Latin, so loosen the default rhythm. */
body { line-height: 1.6; }
.menu-link,
.table td,
.table th { line-height: 1.55; }


/* Every icon renders at 24x24.

   Boxicons sizes itself with `width/height: 1em`, so each icon inherited the
   font-size of whatever it happened to sit in — about 13px inside a small
   button, 15px in a table cell, 22px in the menu. Pinning the box in pixels
   makes the whole set uniform.

   Three selectors because three different rules set icon size, each needing
   to be out-specified on its own terms:
     .bx                             icons-subset.css, the 1em default
     .menu-icon.bx                   core.css sizes menu icons at 1.375rem
     ...layout-menu-toggle i.bx      core.css sizes the collapse chevron too

   This also retires the `bx-sm` helper: with the box fixed in pixels, a
   font-size class no longer changes anything. The two `bx-sm` classes left in
   liff-admin.html are inert. */
.bx,
.menu-icon.bx,
.layout-menu .app-brand .layout-menu-toggle i.bx {
  inline-size: 24px;
  block-size: 24px;
  font-size: 24px;
  flex-shrink: 0;
}


/* ------------------------------------------- 2. semi-dark menu ---- */
:root {
  --admin-menu-bg: #2b2c40;
  --admin-menu-text: #b9bcd4;
  --admin-menu-text-strong: #ffffff;
  --admin-menu-active: #696cff;
  --admin-menu-hover: rgba(255, 255, 255, .06);
  --admin-menu-divider: rgba(255, 255, 255, .08);
}

.layout-menu.bg-menu-theme,
.bg-menu-theme { background-color: var(--admin-menu-bg) !important; }

.bg-menu-theme .menu-link,
.bg-menu-theme .app-brand-text { color: var(--admin-menu-text) !important; }

.bg-menu-theme .menu-item .menu-link:hover,
.bg-menu-theme .menu-item .menu-link:focus {
  background-color: var(--admin-menu-hover) !important;
  color: var(--admin-menu-text-strong) !important;
}

/* Grouped menu: `active` runs up the whole chain, so the solid pill belongs to
   the leaf tab only. The group heading it sits under gets a soft tint instead,
   which still says "you are in this section" without competing with the leaf. */
.bg-menu-theme .menu-item.active > .menu-link:not(.menu-toggle) {
  background: var(--admin-menu-active) !important;
  color: #fff !important;
  box-shadow: 0 2px 6px 0 rgba(105, 108, 255, .4);
}

.bg-menu-theme .menu-item.active > .menu-link.menu-toggle {
  background: var(--admin-menu-hover) !important;
  color: var(--admin-menu-text-strong) !important;
}

.bg-menu-theme .menu-item.active > .menu-link .menu-icon,
.bg-menu-theme .menu-item.active > .menu-link .text-truncate { color: inherit !important; }

/* Sub-item bullets: Sneat's --bs-gray-400 disappears against the dark panel. */
.bg-menu-theme .menu-sub > .menu-item > .menu-link::before {
  background-color: rgba(255, 255, 255, .3);
}
.bg-menu-theme .menu-sub > .menu-item.active > .menu-link::before {
  background-color: #fff;
  box-shadow: none;
}

.bg-menu-theme .menu-inner-shadow {
  background: linear-gradient(var(--admin-menu-bg) 41%, rgba(43, 44, 64, .55) 95%, rgba(43, 44, 64, 0));
}

.bg-menu-theme .menu-divider { border-color: var(--admin-menu-divider) !important; }
.bg-menu-theme .app-brand { border-bottom: 1px solid var(--admin-menu-divider); }
.bg-menu-theme .layout-menu-toggle i { color: var(--admin-menu-text) !important; }

/* Keep the collapse arrow on screen while the menu is collapsed.

   Sneat fades it to `opacity: 0` there and expects hover-expand plus the
   `d-block` that main.js adds after a 300ms hover to bring it back. That
   round trip is too easy to miss, and missing it strands the user in the
   collapsed menu with no visible way out — so pin it visible instead. */
.layout-menu-collapsed .layout-menu .app-brand .layout-menu-toggle {
  display: block !important;
  opacity: 1 !important;
}

/* Chevron points the way it will move the menu: right to reopen. */
.layout-menu-collapsed .layout-menu .app-brand .layout-menu-toggle i {
  transform: rotate(180deg);
}

/* The collapse button is an absolutely positioned circle in .app-brand, sized
   only by its icon and its 7px border. It also carries `.menu-link`, so any
   padding aimed at menu entries has to stay inside .menu-inner or the circle
   stretches into a tall pill. */
.layout-menu .app-brand .layout-menu-toggle {
  padding: 0;
  min-block-size: 0;
  line-height: 0;
}

/* Every group open at once still overruns a laptop viewport. Sneat normally
   hands that scroll to PerfectScrollbar, which the free build does not ship,
   so own the scrolling here instead of relying on menu.js's fallback class. */
.layout-menu .app-brand { flex-shrink: 0; }
.layout-menu .menu-inner {
  flex: 1 1 auto;
  min-block-size: 0;
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* "กางเมนูทั้งหมด" strip, pinned under the scrolling list so it is always
   reachable. The icon and label are swapped by renderMenuExpandAllButton(). */
.layout-menu .menu-footer-toggle {
  flex-shrink: 0;
  padding: .4rem .6rem;
  border-block-start: 1px solid var(--admin-menu-divider);
}

.layout-menu .menu-footer-toggle .menu-link {
  padding: .5rem .75rem;
  border-radius: .375rem;
  cursor: pointer;
}

/* A collapsed sidebar has no group labels to reveal, so the strip goes with
   them rather than sitting there as an icon that appears to do nothing. */
.layout-menu-collapsed:not(.layout-menu-hover) .menu-footer-toggle {
  display: none;
}

/* Menu scrollbar reads as a light scratch on the dark panel. */
.bg-menu-theme .menu-inner::-webkit-scrollbar { width: 5px; }
.bg-menu-theme .menu-inner::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, .16);
  border-radius: 3px;
}
.bg-menu-theme .menu-inner { scrollbar-width: thin; scrollbar-color: rgba(255, 255, 255, .16) transparent; }

/* Tighter rhythm — section 1 loosens line-height for Thai, which the menu
   pays for. Scoped to .menu-inner so the collapse circle keeps its own box. */
.layout-menu .menu-inner > .menu-item { margin-block-start: .1rem; }
.layout-menu .menu-inner .menu-link {
  padding-block: .4rem;
  min-block-size: 2.25rem;
}

.app-brand .brand-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px; height: 38px;
  border-radius: 10px;
  background: linear-gradient(135deg, #696cff, #8592ff);
  color: #fff;
  font-weight: 700;
  font-size: 13px;
  letter-spacing: .5px;
}


/* ------------------------------------------------ 3. login page ---- */
.login-wrap {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: linear-gradient(135deg, #2b2c40 0%, #4b4d70 100%);
}

.login-card {
  width: 100%;
  max-width: 420px;
  margin: 0;
  box-shadow: 0 12px 40px rgba(0, 0, 0, .28);
}

.login-card .card-body { padding: 2.25rem 2rem; }
.login-header { text-align: center; margin-bottom: 1.6rem; }

.login-logo-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 54px; height: 54px;
  margin-bottom: .9rem;
  border-radius: 12px;
  background: linear-gradient(135deg, #696cff, #8592ff);
  color: #fff;
  font-weight: 700;
  letter-spacing: .5px;
}

.login-title { margin: 0 0 .35rem; }
.login-sub { font-size: .85rem; color: var(--bs-secondary-color); margin: 0; }


/* --------------------------------------------------- 4. chat ---- */
.chat-container {
  display: flex;
  height: calc(100vh - 210px);
  min-height: 470px;
  overflow: hidden;
  border-radius: inherit;
}

.chat-sidebar {
  width: 310px;
  flex-shrink: 0;
  border-right: 1px solid var(--bs-border-color);
  display: flex;
  flex-direction: column;
}

.chat-sidebar-header {
  padding: 1rem;
  border-bottom: 1px solid var(--bs-border-color);
}

.chat-user-list { overflow-y: auto; flex: 1; }

.chat-user-item {
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: .75rem 1rem;
  cursor: pointer;
  border-bottom: 1px solid var(--bs-border-color);
  transition: background-color .15s ease;
}
.chat-user-item:hover { background: var(--bs-tertiary-bg); }
.chat-user-item.active { background: rgba(105, 108, 255, .12); }
.chat-user-item.active .chat-user-name { color: #696cff; }

.chat-user-avatar {
  width: 38px; height: 38px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}
.chat-user-info { min-width: 0; flex: 1; }
.chat-user-name { font-weight: 500; font-size: .9rem; }
.chat-user-dept {
  font-size: .75rem;
  color: var(--bs-secondary-color);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.chat-main { flex: 1; display: flex; flex-direction: column; min-width: 0; }

.chat-header {
  padding: .85rem 1.25rem;
  border-bottom: 1px solid var(--bs-border-color);
  display: flex;
  align-items: center;
  gap: .75rem;
}

.chat-messages {
  flex: 1;
  overflow-y: auto;
  padding: 1.25rem;
  display: flex;
  flex-direction: column;
  gap: .5rem;
  background: var(--bs-tertiary-bg);
}

.chat-msg-row { display: flex; }
.chat-msg-row.user { justify-content: flex-start; }
.chat-msg-row.admin { justify-content: flex-end; }

.chat-bubble {
  padding: .6rem .9rem;
  border-radius: .75rem;
  font-size: .9rem;
  word-break: break-word;
  box-shadow: 0 1px 2px rgba(75, 70, 92, .08);
}
.chat-msg-row.admin .chat-bubble { background: #696cff; color: #fff; }
.chat-msg-row.user .chat-bubble { background: var(--bs-body-bg); color: var(--bs-body-color); }

.chat-time {
  font-size: .6875rem;
  color: var(--bs-secondary-color);
  margin-top: .25rem;
}

.chat-input-area {
  padding: .85rem 1.25rem;
  border-top: 1px solid var(--bs-border-color);
}

.chat-empty-state {
  margin: auto;
  text-align: center;
  color: var(--bs-secondary-color);
}

@media (max-width: 991.98px) {
  .chat-container { flex-direction: column; height: auto; }
  .chat-sidebar {
    width: 100%;
    border-right: 0;
    border-bottom: 1px solid var(--bs-border-color);
    max-height: 250px;
  }
  .chat-messages { min-height: 340px; }
}


/* ------------------------------------- 5. calendar & modal hero ---- */
.cal-modal-hero {
  position: relative;
  flex-shrink: 0;
  padding: 1.5rem;
  padding-right: 3rem;   /* room for the close button */
  background: linear-gradient(135deg, #696cff, #8592ff);
}
/* The hero is not a .modal-header, so it misses the theme's placement rule and
   inherits only `.modal .btn-close`'s translate + hover shift — which the hero's
   overflow:hidden then clips. Pin it where every other modal puts its close
   button, and drop the hover movement. */
.cal-modal-hero .btn-close {
  position: absolute;
  top: .9375rem;
  right: .8125rem;
  transform: none;
  transition: none;
}
.cal-modal-hero .btn-close:hover,
.cal-modal-hero .btn-close:focus,
.cal-modal-hero .btn-close:active {
  transform: none;
}
.cal-modal-avatar {
  width: 60px; height: 60px;
  border-radius: 50%;
  object-fit: cover;
  border: 3px solid rgba(255, 255, 255, .85);
  background: #fff;
}

/* Day list — one row per entry, every field on it, nothing to click open. */
.cal-day-table { min-width: 900px; }
.cal-day-table th { white-space: nowrap; }
.cal-day-table td { vertical-align: top; }
.cal-day-row-avatar {
  width: 34px; height: 34px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--bs-tertiary-bg);
  flex-shrink: 0;
}
/* The reason gets a row of its own under its entry: free text needs the width.
   Bootstrap draws the rule as a border-bottom on the cells above, so the line
   between an entry and its own reason is dropped there, not here — the reason
   row keeps its own bottom border, which is what separates one entry from the
   next. */
.cal-day-table .cal-day-row-open > td { border-bottom-width: 0; }
.cal-day-reason-row td {
  padding-top: 0;
  white-space: pre-wrap;
  word-break: break-word;
}

/* Detail card — label/value pairs in two columns, one column on phones. */
.cal-detail-section + .cal-detail-section { margin-top: 1.5rem; }
.cal-detail-heading {
  display: flex;
  align-items: center;
  gap: .4rem;
  margin-bottom: .75rem;
  padding-bottom: .5rem;
  border-bottom: 1px solid var(--bs-border-color);
  font-size: .8125rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .03em;
  color: var(--bs-secondary-color);
}
.cal-detail-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: .85rem 1.5rem;
}
.cal-detail-section-full .cal-detail-grid { grid-template-columns: 1fr; }
.cal-detail-wide { grid-column: 1 / -1; }
.cal-detail-label {
  font-size: .75rem;
  color: var(--bs-secondary-color);
  margin-bottom: .15rem;
}
.cal-detail-value {
  font-weight: 600;
  word-break: break-word;
}
.cal-detail-note {
  padding: .65rem .85rem;
  border: 1px solid var(--bs-border-color);
  border-radius: .5rem;
  background: var(--bs-tertiary-bg);
  white-space: pre-wrap;
  word-break: break-word;
}

@media (max-width: 575.98px) {
  .cal-detail-grid { grid-template-columns: 1fr; }
}

/* Approval timeline — one step per tier, dot colour carries the state. */
.cal-timeline {
  list-style: none;
  margin: 0;
  padding: 0 0 0 1.25rem;
  border-left: 2px solid var(--bs-border-color);
}
.cal-timeline-step {
  position: relative;
  padding-bottom: 1rem;
  font-size: .875rem;
}
.cal-timeline-step:last-child { padding-bottom: 0; }
.cal-timeline-step::before {
  content: "";
  position: absolute;
  left: calc(-1.25rem - 1px - .35rem);   /* centred on the 2px rail */
  top: .3rem;
  width: .7rem; height: .7rem;
  border-radius: 50%;
  background: var(--bs-border-color);
  box-shadow: 0 0 0 3px var(--bs-paper-bg);
}
.cal-timeline-done::before    { background: #71dd37; }
.cal-timeline-pending::before { background: #ffab00; }
.cal-timeline-title { font-weight: 600; }
.cal-timeline-line { line-height: 1.5; }

/* FullCalendar ships its own palette; align it with the Sneat surface. */
#hrCalendar {
  min-width: 800px;    /* the grid needs the width; .table-responsive scrolls */
  min-height: 600px;
  --fc-border-color: var(--bs-border-color);
  --fc-page-bg-color: transparent;
  --fc-neutral-bg-color: var(--bs-tertiary-bg);
  --fc-today-bg-color: rgba(105, 108, 255, .08);
}
#hrCalendar .fc-toolbar-title { font-size: 1.05rem; font-weight: 600; }
#hrCalendar .fc-button {
  background: var(--bs-body-bg);
  border-color: var(--bs-border-color);
  color: var(--bs-body-color);
  text-transform: none;
  box-shadow: none;
}
#hrCalendar .fc-button:hover { background: var(--bs-tertiary-bg); }
#hrCalendar .fc-button-primary:not(:disabled).fc-button-active,
#hrCalendar .fc-button-primary:not(:disabled):active {
  background: #696cff;
  border-color: #696cff;
  color: #fff;
}
/* Month cells hold exactly what dayMaxEvents: 4 allows — four events and the
   "+N more" line — inside CAL_ROW_HEIGHT (admin-approvals.js), which is 92px.
   The budget: date number ~16px, four events ~58px, the more-link ~15px.
   Everything below trims the vertical rhythm that body{line-height:1.6} (set
   for Thai text) inflates. Loosening any of it means raising CAL_ROW_HEIGHT to
   match, or the events spill out of their cell. */
#hrCalendar .fc-daygrid-day-number {
  padding: 1px 4px;
  font-size: .75rem;
  line-height: 1.2;
}
#hrCalendar .fc-daygrid-day-events { margin-bottom: 0; }
#hrCalendar .fc-daygrid-more-link {
  border: 0;
  padding: 0 4px;
  font-size: .7rem;
  line-height: 1.25;
}
#hrCalendar .fc-event {
  border: 0;
  padding: 0 4px;
  font-size: .7rem;
  line-height: 1.2;
}
/* The day list stands in for the month grid: drop the grid but keep the
   toolbar, and collapse the height the grid was holding open — otherwise the
   list is pushed down past an empty block hundreds of pixels tall. That height
   is FullCalendar's own inline style (from the `height` option), so !important
   is the only way over it; min-width stays, the toolbar is that wide anyway. */
#hrCalendar.cal-grid-hidden {
  min-height: 0;
  height: auto !important;
}
#hrCalendar.cal-grid-hidden .fc-view-harness { display: none; }


/* --------------------------------------------------- 6. misc ---- */
/* Panes fade in when switched, matching the theme's page transitions. */
.admin-tab-pane { animation: adminFadeIn .2s ease; }
@keyframes adminFadeIn {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}

/* Member list (จัดการสมาชิก). The header sets a percentage width per column so
   the browser cannot hand all the slack to whichever value happens to be
   longest; this floor keeps those percentages from squeezing into nonsense on
   a narrow screen, letting .table-responsive scroll instead. Values wrap —
   the wrapper deliberately carries no .text-nowrap. */
.members-table { min-inline-size: 1020px; }
.members-table td { word-break: break-word; }

/* Admin role list (สิทธิ์ผู้ดูแลระบบ). Same shape as the member list. */
.admin-roles-table { min-inline-size: 900px; }
.admin-roles-table td { word-break: break-word; }

/* The LINE id is a 33-character opaque string that no one reads — enough of it
   to recognise, the rest on hover, and the row keeps its proportions. */
.admin-roles-table .admin-lineid {
  display: inline-block;
  max-inline-size: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}

/* The LINE photo sits on top of the initial, so a 404 from LINE's CDN falls
   back to the letter underneath instead of a torn-image icon. */
.members-table .member-avatar-img,
.admin-roles-table .member-avatar-img {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

/* Hidden members stay legible but visibly out of play. */
.members-table tr.member-row-deleted td { opacity: .62; }

/* Sortable table headers in the approvals list. */
.table thead th[onclick] { cursor: pointer; user-select: none; }
.table thead th[onclick]:hover { color: #696cff; }

/* ------------------------------------- 7. copy-to-clipboard bits ---- */

/* A member's photo doubles as a copy button for their LINE User ID. The ring
   on hover is the only thing announcing it — the avatar keeps its size so the
   row does not shift under the pointer. */
.member-avatar-copy {
  cursor: pointer;
  border-radius: 50%;
  transition: box-shadow .15s ease;
}
.member-avatar-copy:hover,
.member-avatar-copy:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--bs-body-bg, #fff), 0 0 0 4px #696cff;
}

/* Confirmation lands on the avatar itself as well as in the toast, so it is
   obvious which row was copied when several are clicked in a row. */
.member-avatar-copy.is-copied::after {
  content: "✓";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: #71dd37;
  color: #fff;
  font-size: 1rem;
  font-weight: 700;
}

#adminToastHost {
  position: fixed;
  inset-block-end: 1.25rem;
  inset-inline-end: 1.25rem;
  z-index: 2000;
  display: flex;
  flex-direction: column;
  gap: .5rem;
  pointer-events: none;
}

/* Inverted against the page so it reads in either theme without a second
   palette: Bootstrap already swaps these two variables on data-bs-theme. */
.admin-toast {
  max-inline-size: min(90vw, 30rem);
  padding: .625rem .875rem;
  border-radius: .5rem;
  background: var(--bs-body-color, #2f3349);
  color: var(--bs-body-bg, #fff);
  font-size: .8125rem;
  word-break: break-all;
  box-shadow: 0 .25rem .875rem rgba(0, 0, 0, .25);
  opacity: 1;
  transition: opacity .35s ease, transform .35s ease;
}
.admin-toast-success { border-inline-start: 3px solid #71dd37; }
.admin-toast.is-leaving {
  opacity: 0;
  transform: translateY(.5rem);
}
