@charset "UTF-8";
/* ============================================================================
   style-mobile.css — Disway Event admin CMS, mobile layer
   ----------------------------------------------------------------------------
   ADDITIVE ONLY. Nothing in the vendor theme is edited, so the next theme
   update (style.css / style.min.css / icons.min.css) drops in unchanged.

   LOAD ORDER — this file must come LAST in <head>:
       style.min.css  →  icons.min.css  →  brand.css  →  style-mobile.css

   SCOPE — every rule sits inside a max-width media query, so desktop renders
   byte-identical to today. Breakpoints match Bootstrap 5's own so the rules
   compose with the theme's instead of fighting them:
       <= 991.98px   tablet + phone (sidebar is already off-canvas here —
                     config.js flips data-sidebar-size=full below 1200px)
       <= 767.98px   phone — the bulk of the work
       <= 575.98px   small phone

   NO MARKUP CHANGES REQUIRED. Three opt-in helpers at the bottom need one
   class added in a view; they are clearly marked and safe to ignore.

   Dark mode: every colour is read from the theme's own custom properties, so
   the sheet follows data-bs-theme without needing a second palette.
   ========================================================================== */


/* ---------------------------------------------------------------------------
   0. Tunables — change these, not the rules below
   ------------------------------------------------------------------------ */
:root {
  /* 44px is the Apple HIG / WCAG 2.5.5 minimum touch target. The theme's
     .btn lands at ~33px and .btn-sm at ~27px. */
  --dm-tap: 44px;
  /* Dense variant for in-table row actions, where a full 44px makes every row
     tower. WCAG 2.5.8 (AA) asks for 24px plus spacing, so 40px is comfortably
     compliant while keeping list density usable. */
  --dm-tap-dense: 40px;
  --dm-gutter: 0.75rem;
  --dm-topbar-height: 56px;
}


/* ===========================================================================
   TABLET AND PHONE  (<= 991.98px)
   ========================================================================= */
@media (max-width: 991.98px) {

  /* -- 1. Horizontal overflow guard --------------------------------------
     A single wide table, a long unbroken word, or an ApexCharts canvas that
     has not re-measured yet drags the whole page sideways, which then makes
     the sticky topbar look detached as you scroll.

     `clip` rather than `hidden`: overflow:hidden on an ancestor silently
     kills `position: sticky` on .navbar-custom and .main-menu. overflow:clip
     does not. The `hidden` line is the fallback for anything older. */
  body {
    overflow-x: hidden;
    overflow-x: clip;
  }

  /* Flex children default to min-width:auto, which refuses to shrink below
     their content — this is the actual mechanism behind most "my admin page
     scrolls sideways" reports. */
  .layout-wrapper,
  .page-content,
  .page-content > .container-fluid {
    min-width: 0;
  }

  /* Long slugs, URLs and e-mail addresses. Deliberately NOT applied to table
     cells — those are handled by the nowrap-plus-scroll rule in section 9. */
  .card-body,
  .dropdown-item {
    overflow-wrap: anywhere;
  }

  /* -- 2. Off-canvas sidebar ---------------------------------------------
     100vh on iOS Safari is the LARGE viewport — the last two or three menu
     items sit underneath the browser chrome and cannot be reached. dvh
     tracks the visible area instead. */
  .main-menu {
    height: 100dvh;
    min-height: 100dvh;
    max-height: 100dvh;
  }

  /* Home-indicator clearance on gesture-nav phones. */
  .main-menu [data-simplebar] {
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }

  /* Menu rows are ~38px tall (8px padding + 15.2px text). */
  .app-menu .menu-item .menu-link {
    min-height: var(--dm-tap);
  }

  /* The sidebar sits at z-index 1055 and app.js injects #custom-backdrop
     behind it. Give the backdrop the same easing the drawer has, so the two
     move together instead of the dim snapping in first. */
  #custom-backdrop {
    transition: opacity 0.25s ease-in-out;
  }
}


/* ===========================================================================
   PHONE  (<= 767.98px)
   ========================================================================= */
@media (max-width: 767.98px) {

  /* -- 3. Topbar ----------------------------------------------------------
     One variable resizes the whole bar: the theme derives .navbar-custom
     min-height, .logo-box height, .topbar .nav-link height AND the sidebar's
     scroll area — calc(100% - var(--bs-topbar-height)) — from it. 70px is a
     desktop number; on an 844pt phone it is 8% of the viewport spent on chrome. */
  :root {
    --bs-topbar-height: var(--dm-topbar-height);
  }

  /* 60px of dead width next to a 24px glyph, on the narrowest screen. */
  .navbar-custom .button-toggle-menu {
    width: var(--dm-tap);
    height: var(--dm-tap);
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  /* Topbar icon buttons are full-height anchors with no width — easy to miss
     with a thumb, and they collide with each other. */
  .navbar-custom .topbar .nav-link {
    min-width: var(--dm-tap);
    justify-content: center;
  }

  /* -- 4. Type ------------------------------------------------------------
     The theme's base is 0.875rem (14px). Fine at arm's length on a monitor,
     thin on a phone held at 30cm. 15px keeps the layout proportions. */
  body {
    font-size: 0.9375rem;
  }

  /* -- 5. iOS auto-zoom on focus -----------------------------------------
     The single highest-impact fix here, and it is pure CSS. Mobile Safari
     zooms the viewport whenever a focused text field computes to less than
     16px — the theme's fields are 14px, so every tap on an input jumps the
     page and leaves it zoomed after blur. 16px is a threshold, not a
     preference: 15.9px still zooms.

     line-height is pulled back so the controls do not grow as much as the
     type does. */
  .form-control,
  .form-control-sm,
  .form-select,
  .form-select-sm,
  textarea.form-control,
  input[type="text"],
  input[type="search"],
  input[type="email"],
  input[type="tel"],
  input[type="url"],
  input[type="number"],
  input[type="password"],
  input[type="date"],
  input[type="time"],
  input[type="datetime-local"],
  select,
  textarea,
  .input-group-text,
  .select2-container .select2-selection--single .select2-selection__rendered,
  .select2-search__field,
  .flatpickr-input,
  .dataTables_filter input,
  .dataTables_length select,
  .ql-editor {
    font-size: 16px;
  }

  .form-control,
  .form-select {
    line-height: 1.35;
  }

  /* -- 6. Touch targets ---------------------------------------------------
     .btn is ~33px tall, .btn-sm ~27px. Both are below every published
     minimum. min-height (not padding) so icon-only and text buttons agree. */
  .btn {
    min-height: var(--dm-tap);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.375rem;
  }

  /* Dense variant for row actions — see the note on --dm-tap-dense. */
  .btn-sm,
  .btn-group-sm > .btn {
    min-height: var(--dm-tap-dense);
    min-width: var(--dm-tap-dense);
    font-size: 0.875rem;
  }

  .dropdown-item {
    min-height: var(--dm-tap);
    display: flex;
    align-items: center;
    padding-block: 0.5rem;
  }

  .pagination .page-link {
    min-width: var(--dm-tap);
    min-height: var(--dm-tap);
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  /* 14px checkboxes are the mass-action selectors in the list views — the
     hardest thing on the page to hit accurately. */
  .form-check-input {
    width: 1.15rem;
    height: 1.15rem;
  }

  .form-check {
    min-height: 1.6rem;
  }

  /* Modal and alert dismiss buttons are ~16px of clickable glyph. */
  .btn-close {
    min-width: var(--dm-tap-dense);
    min-height: var(--dm-tap-dense);
    background-position: center;
  }

  /* -- 7. Density ---------------------------------------------------------
     1rem of card padding on each side costs 32px of a 390px screen — 8% of
     the width, before any content. */
  .card {
    --bs-card-spacer-x: 0.875rem;
    --bs-card-spacer-y: 0.875rem;
    --bs-card-cap-padding-x: 0.875rem;
    margin-bottom: 0.75rem;
  }

  .page-content > .container-fluid,
  .navbar-custom .topbar {
    padding-inline: var(--dm-gutter);
  }

  .row {
    --bs-gutter-x: 0.75rem;
  }

  /* -- 8. Page header -----------------------------------------------------
     Title and action button share one flex row and squeeze each other; the
     button ends up two words tall next to a clipped heading. */
  .page-title-box {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 0.75rem;
  }

  .page-title-box h4,
  .page-title-box .page-title {
    flex: 1 1 100%;
    margin-bottom: 0;
  }

  /* -- 9. Tables ----------------------------------------------------------
     An admin list has 6-8 columns. At 390px the browser will not drop any of
     them, so it wraps every cell instead and you get 90px-tall rows of
     two-letter fragments. A horizontal scroller is the honest answer.

     Two cases are handled:
       a) .table-responsive already in the markup — widen it to the card edge
          so more columns are visible per swipe.
       b) a bare <table class="table"> inside .card-body — :has() turns the
          card body into the scroller without touching the view. Browsers
          without :has() simply keep today's behaviour. */
  .card-body:has(> .table-responsive),
  .card-body:has(> .table) {
    padding-inline: 0;
  }

  .card-body:has(> .table-responsive) > :not(.table-responsive),
  .card-body:has(> .table) > :not(table) {
    padding-inline: var(--bs-card-spacer-x);
  }

  .card-body:has(> .table) {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }

  /* Without a floor the table just compresses instead of scrolling. */
  .card-body > .table,
  .table-responsive > .table {
    min-width: 34rem;
  }

  /* Nothing in the table wraps. This is the rule that actually fixes mobile
     list density, and it is worth being explicit about the trade:

       wrap    — every column is squeezed to fit 390px, so "Fin Business
                 Forum — Sesi Sore" becomes five stacked fragments, rows are
                 90-140px tall and ragged, and you can only see ~4 rows.
       nowrap  — one line per row, uniform ~56px, ~10 rows visible, and a long
                 name simply extends the table you are already swiping.

     Nothing is truncated either way; nowrap just moves the overflow onto an
     axis the user is already scrolling. Pair with .table-sticky-first so the
     identifying column stays put during that swipe. */
  .table > :not(caption) > * > * {
    white-space: nowrap;
  }

  /* Scroll affordance. Two local-attachment gradients paint the fades and two
     scroll-attachment radials paint the shadows, so each edge hint disappears
     on its own once you reach that end — no JS, no extra element. */
  .table-responsive {
    background:
      linear-gradient(to right, var(--bs-secondary-bg) 30%, rgba(0, 0, 0, 0)) left center / 24px 100% no-repeat,
      linear-gradient(to left, var(--bs-secondary-bg) 30%, rgba(0, 0, 0, 0)) right center / 24px 100% no-repeat,
      radial-gradient(farthest-side at 0 50%, rgba(0, 0, 0, 0.14), rgba(0, 0, 0, 0)) left center / 12px 100% no-repeat,
      radial-gradient(farthest-side at 100% 50%, rgba(0, 0, 0, 0.14), rgba(0, 0, 0, 0)) right center / 12px 100% no-repeat;
    background-attachment: local, local, scroll, scroll;
  }

  /* -- 10. DataTables controls -------------------------------------------
     The length select, the search box and the export buttons are laid out as
     a 3-across .row with text-align left/right. Below 576px the theme only
     centres the text, which leaves three stubby half-width controls. */
  div.dataTables_wrapper div.dataTables_length,
  div.dataTables_wrapper div.dataTables_filter {
    width: 100%;
    text-align: left;
  }

  /* nowrap matters: these labels are bare text nodes around the control
     ("Tampil [10] data"), and as flex items they otherwise shrink and break
     mid-word into "Tam / pil" and "dat / a". */
  div.dataTables_wrapper div.dataTables_length label,
  div.dataTables_wrapper div.dataTables_filter label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    margin-bottom: 0.5rem;
    white-space: nowrap;
  }

  /* The page-length select stays compact — it holds two digits. */
  div.dataTables_wrapper div.dataTables_length select {
    flex: 0 0 auto;
    width: auto;
  }

  /* The search box takes everything that is left. */
  div.dataTables_wrapper div.dataTables_filter input {
    flex: 1 1 auto;
    min-width: 0;
    width: auto;
    margin-left: 0;
  }

  div.dt-buttons {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    width: 100%;
    margin-bottom: 0.75rem;
  }

  .dataTables_info,
  .dataTables_paginate {
    text-align: center;
  }

  /* display:flex is the load-bearing line. The theme sets
     `.dataTables_paginate ul { display: block }` below 768px, which drops the
     list out of flex layout — every page number becomes a full-width
     list-item and the pager turns into a 22-row vertical stack that pushes
     the footer off the screen. This selector is more specific, so no
     !important is needed to win. */
  .dataTables_paginate ul.pagination {
    display: flex;
    justify-content: center;
    flex-wrap: wrap;
    gap: 0.25rem;
  }

  /* -- 11. Forms ----------------------------------------------------------- */
  .form-label {
    margin-bottom: 0.375rem;
    font-weight: 600;
  }

  /* An input-group that wraps splits the addon onto its own line with the
     rounded corners still suppressed — it reads as a rendering bug. */
  .input-group {
    flex-wrap: nowrap;
  }

  .input-group > .form-control,
  .input-group > .form-select {
    min-width: 0;
  }

  /* Plugin widgets that hard-code a pixel width in their own stylesheet. */
  .select2-container,
  .bootstrap-select,
  .flatpickr-input,
  .daterangepicker,
  .bootstrap-touchspin {
    max-width: 100%;
  }

  .select2-container {
    width: 100% !important;
  }

  /* Calendar pop-ups are absolutely positioned from the field and run off the
     right edge on a narrow screen. */
  .flatpickr-calendar,
  .daterangepicker,
  .datepicker-dropdown {
    max-width: calc(100vw - 1.5rem);
  }

  /* Editors: toolbars are a single nowrap row of ~20 buttons. */
  .ql-toolbar.ql-snow,
  .tox-toolbar__primary {
    flex-wrap: wrap !important;
  }

  .ql-container {
    min-height: 12rem;
  }

  .dropzone {
    min-height: 8rem;
    padding: 1rem;
  }

  /* -- 12. Dropdowns ------------------------------------------------------
     Notification and profile menus carry a fixed width (.dropdown-menu-lg is
     320px) and a long list with no height cap. */
  .dropdown-menu {
    max-width: calc(100vw - 1rem);
    max-height: 70vh;
    max-height: 70dvh;
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  .dropdown-menu-lg,
  .dropdown-menu-xl {
    width: auto;
    min-width: 0;
  }

  /* -- 13. Footer ---------------------------------------------------------
     The theme pins .footer to height:60px and then centres the text below
     768px — two lines of links overflow the box and sit on top of the page
     content. */
  .footer {
    height: auto;
    min-height: 60px;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.25rem 1rem;
    padding: 0.75rem 1rem calc(0.75rem + env(safe-area-inset-bottom, 0px));
    text-align: center;
  }

  /* -- 14. Charts and overlays -------------------------------------------- */
  .apex-charts,
  .apexcharts-canvas,
  .chartjs-render-monitor {
    max-width: 100% !important;
  }

  #toast-container > div {
    width: calc(100vw - 2rem) !important;
  }

  .swal2-popup {
    width: calc(100vw - 2rem) !important;
  }
}


/* ===========================================================================
   SMALL PHONE  (<= 575.98px)
   ========================================================================= */
@media (max-width: 575.98px) {

  /* -- 15. Modals as sheets -----------------------------------------------
     A centred .modal-dialog on a 360px screen is a small box with 16px of
     margin, an inner scroll area, and its own buttons below the fold. The
     modals here are forms, so full-screen is the right shape. .modal-sm
     (confirm dialogs) stays centred — a full-screen "Delete this?" is worse. */
  .modal-dialog:not(.modal-sm):not([class*="modal-fullscreen"]) {
    width: 100%;
    max-width: none;
    height: 100%;
    margin: 0;
  }

  .modal-dialog:not(.modal-sm):not([class*="modal-fullscreen"]) .modal-content {
    height: 100%;
    border: 0;
    border-radius: 0;
  }

  .modal-dialog:not(.modal-sm):not([class*="modal-fullscreen"]) .modal-body {
    overflow-y: auto;
  }

  /* Save / Cancel stay reachable without scrolling a long form to the end. */
  .modal-footer {
    position: sticky;
    bottom: 0;
    z-index: 2;
    background: var(--bs-modal-bg, var(--bs-body-bg));
    border-top: 1px solid var(--bs-border-color);
    padding-bottom: calc(0.75rem + env(safe-area-inset-bottom, 0px));
  }

  /* -- 16. Breadcrumb -----------------------------------------------------
     Costs a full line to restate a path the sidebar already shows. Delete
     this block if the team wants it back. */
  .page-title-box .breadcrumb {
    display: none;
  }

  /* -- 17. Stat cards -----------------------------------------------------
     Avatars set height/width but not flex-basis, so inside the usual
     "label + figure on the left, icon on the right" flex row the icon is the
     item that gives way: on a col-6 tile it squashes to an oval. Nothing
     here changes the size — it only stops the box from being compressed.

     (Heading sizes are deliberately left alone. The theme's h4 is already
     1.125rem and h3 is calc(1.275rem + 0.3vw); shrinking them further makes
     the dashboard figures harder to read, and enlarging them wraps long
     rupiah amounts mid-number.) */
  .avatar-xs,
  .avatar-sm,
  .avatar-md,
  .avatar-lg,
  .avatar-xl {
    flex-shrink: 0;
  }

  /* 4.5rem and 6rem are 20% and 27% of a 360px screen. */
  .avatar-lg {
    height: 3.5rem;
    width: 3.5rem;
  }

  .avatar-xl {
    height: 4.5rem;
    width: 4.5rem;
  }
}


/* ===========================================================================
   OPT-IN HELPERS — the only three that need a class in a view. Everything
   above works with the markup exactly as it is today.
   ========================================================================= */

/* A. .table-sticky-first  — on <table class="table table-sticky-first">
      Pins the identifying column (title, name, invoice no.) while the rest
      scrolls, so a sideways swipe never loses the row you are reading.

      Second selector: on a list with mass actions the first cell is the
      row checkbox, and pinning that alone is useless. :has() detects it and
      pins the next cell — the actual name column — as well. */
@media (max-width: 767.98px) {
  .table-sticky-first > :not(caption) > * > *:first-child,
  .table-sticky-first > :not(caption) > * > *:first-child:has(.form-check-input) + * {
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--bs-secondary-bg);
  }

  /* The checkbox cell is pinned at 0 and the name column starts exactly where
     it ends. Width is locked from all three sides and the cell's own padding
     trimmed, because a table cell treats `width` as a suggestion — if the
     rendered cell ends up even 3px wider than the offset below, that gap
     shows a 3px strip of the scrolled-under column between the two pinned
     cells. One custom property keeps the two numbers from drifting apart. */
  .table-sticky-first {
    --dm-checkbox-col: 2.75rem;
  }

  .table-sticky-first > :not(caption) > * > *:first-child:has(.form-check-input) {
    width: var(--dm-checkbox-col);
    min-width: var(--dm-checkbox-col);
    max-width: var(--dm-checkbox-col);
    padding-inline: 0.5rem;
  }

  .table-sticky-first > :not(caption) > * > *:first-child:has(.form-check-input) + * {
    left: var(--dm-checkbox-col);
  }

  /* Edge shadow only on whichever cell is the last pinned one. */
  .table-sticky-first > :not(caption) > * > *:first-child:not(:has(.form-check-input)),
  .table-sticky-first > :not(caption) > * > *:first-child:has(.form-check-input) + * {
    box-shadow: 1px 0 0 var(--bs-border-color);
  }
}

/* B. .mobile-sticky-actions  — on the wrapper around a form's Save/Cancel
      (typically the .card-footer, or the .text-end div at the end of a form).
      Docks them to the bottom of the screen instead of burying them under
      fifteen fields. */
@media (max-width: 767.98px) {
  .mobile-sticky-actions {
    position: sticky;
    bottom: 0;
    z-index: 5;
    display: flex;
    gap: 0.5rem;
    background: var(--bs-secondary-bg);
    border-top: 1px solid var(--bs-border-color);
    margin-inline: calc(var(--bs-card-spacer-x) * -1);
    padding: 0.75rem var(--bs-card-spacer-x)
             calc(0.75rem + env(safe-area-inset-bottom, 0px));
  }

  .mobile-sticky-actions > .btn {
    flex: 1 1 0;
  }
}

/* C. .mobile-stack  — on any row of buttons or inline controls that should
      become one full-width column on a phone. */
@media (max-width: 767.98px) {
  .mobile-stack {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.5rem;
  }

  .mobile-stack > .btn,
  .mobile-stack > .btn-group {
    width: 100%;
  }
}
