/* =========================================================================
   ELSHADDAI EXTERIOR INOVASI — application theme
   Loads AFTER bootstrap.min.css. Nothing here re-implements Bootstrap; it
   maps our design tokens onto Bootstrap 5.3's own CSS variables and adds the
   components Bootstrap has no equivalent for (sidebar, sheet rail, signature
   strip, KPI strip, RAB input grid).

   Use Bootstrap classes for anything Bootstrap already does — grid, buttons,
   forms, tables, modals, utilities. Add a custom class only when Bootstrap
   genuinely lacks the component.
   ========================================================================= */

/* ============================ 1. TOKENS ============================ */
:root{
  --brand:#1A4C9C; --brand-600:#14407F; --brand-700:#0F2748; --brand-50:#EAF1FB;
  --red:#C62B25;   --red-50:#FCEDEC;

  --bg:#F1F4F8; --panel:#FFFFFF; --panel-2:#F7F9FC; --panel-3:#EDF1F6;
  --ink:#13202E; --ink-2:#455969; --muted:#7B8C9C;
  --line:#DCE3EB; --line-2:#C2CEDA;

  --ok:#1A7A4D;   --ok-50:#E5F2EB;
  --warn:#9C6300; --warn-50:#FBF0DC;
  --crit:#B4302A; --crit-50:#FBEBEA;

  --side:#0F2748; --side-2:#16345C; --side-ink:#C7D6EA; --side-ink-2:#89A2C2;

  --sans:"Plus Jakarta Sans",ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  --mono:"IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,monospace;

  --r:6px;
  --shadow-1:0 1px 2px rgba(19,32,46,.06);
  --shadow-2:0 2px 4px rgba(19,32,46,.06),0 12px 28px -18px rgba(19,32,46,.35);
  --side-w:236px;
}
html[data-theme="dark"]{
  --brand:#6FA3EE; --brand-600:#8CB7F3; --brand-700:#0C1A2C; --brand-50:#152741;
  --red:#EE8781;   --red-50:#2E1A18;

  --bg:#0C1116; --panel:#151D25; --panel-2:#1B242D; --panel-3:#212C36;
  --ink:#E3EAF1; --ink-2:#A7B7C6; --muted:#7A8A99;
  --line:#26313B; --line-2:#35434F;

  --ok:#63C595;   --ok-50:#12291F;
  --warn:#D9A353; --warn-50:#2A2113;
  --crit:#E98A83; --crit-50:#2C1917;

  --side:#0A1725; --side-2:#12283F; --side-ink:#B6C8DC; --side-ink-2:#7B93AE;

  --shadow-1:0 1px 2px rgba(0,0,0,.4);
  --shadow-2:0 2px 4px rgba(0,0,0,.4),0 12px 28px -18px rgba(0,0,0,.9);
}

/* ============ 2. TOKENS → BOOTSTRAP =============
   Bootstrap 5.3 is themed entirely through --bs-* variables. Setting them here
   means every stock Bootstrap component picks up the brand automatically, and
   no component stylesheet is ever overridden by hand.
   The app also sets data-bs-theme alongside data-theme so Bootstrap's own dark
   mode switches with ours. */
:root{
  --bs-font-sans-serif:var(--sans);
  --bs-font-monospace:var(--mono);
  --bs-body-font-size:.875rem;
  --bs-body-bg:var(--bg);
  --bs-body-color:var(--ink);
  --bs-emphasis-color:var(--ink);
  --bs-secondary-color:var(--ink-2);
  --bs-tertiary-color:var(--muted);
  --bs-secondary-bg:var(--panel-2);
  --bs-tertiary-bg:var(--panel-3);
  --bs-border-color:var(--line);
  --bs-border-radius:var(--r);
  --bs-primary:var(--brand);
  --bs-link-color:var(--brand);
  --bs-link-hover-color:var(--brand-600);
  --bs-focus-ring-color:color-mix(in srgb,var(--brand) 24%,transparent);
  --bs-focus-ring-width:3px;
}

/* Button variants. 5.3 scopes colours to per-variant --bs-btn-* vars, so this
   is the supported way to brand them. */
.btn{--bs-btn-font-size:.8125rem;--bs-btn-font-weight:600;--bs-btn-padding-y:.44rem;--bs-btn-padding-x:.8rem;--bs-btn-border-radius:5px}
.btn-sm{--bs-btn-font-size:.75rem;--bs-btn-padding-y:.3rem;--bs-btn-padding-x:.6rem}
.btn-primary{
  --bs-btn-bg:var(--brand);--bs-btn-border-color:var(--brand);--bs-btn-color:#fff;
  --bs-btn-hover-bg:var(--brand-600);--bs-btn-hover-border-color:var(--brand-600);--bs-btn-hover-color:#fff;
  --bs-btn-active-bg:var(--brand-600);--bs-btn-active-border-color:var(--brand-600);
  --bs-btn-disabled-bg:var(--brand);--bs-btn-disabled-border-color:var(--brand);
}
html[data-theme="dark"] .btn-primary{--bs-btn-color:#0B1622;--bs-btn-hover-color:#0B1622}
.btn-outline-secondary{
  --bs-btn-color:var(--ink);--bs-btn-border-color:var(--line-2);
  --bs-btn-hover-bg:var(--panel-2);--bs-btn-hover-border-color:var(--muted);--bs-btn-hover-color:var(--ink);
  --bs-btn-active-bg:var(--panel-3);--bs-btn-active-border-color:var(--muted);--bs-btn-active-color:var(--ink);
}
.btn-outline-danger{--bs-btn-color:var(--red);--bs-btn-border-color:var(--red);--bs-btn-hover-bg:var(--red);--bs-btn-hover-border-color:var(--red)}
.btn svg{width:15px;height:15px;vertical-align:-2px}
.btn-sm svg{width:13px;height:13px}

/* Forms, cards, tables, alerts — stock components, brand colours */
.form-control,.form-select{--bs-border-color:var(--line-2);background-color:var(--panel);color:var(--ink);font-size:.84rem}
.form-control:focus,.form-select:focus{border-color:var(--brand);box-shadow:0 0 0 3px color-mix(in srgb,var(--brand) 16%,transparent)}
.form-label{font-size:.75rem;font-weight:600;color:var(--ink-2);margin-bottom:.3rem}
.form-check-input:checked{background-color:var(--brand);border-color:var(--brand)}
.form-text{font-size:.72rem;color:var(--muted)}
.invalid-feedback{font-size:.72rem}
.card{--bs-card-bg:var(--panel);--bs-card-border-color:var(--line);--bs-card-cap-bg:var(--panel-2)}
.table{--bs-table-bg:transparent;--bs-table-color:var(--ink);--bs-table-border-color:var(--line);--bs-table-hover-bg:var(--panel-2);font-size:.8125rem}
.table>thead{--bs-table-bg:var(--panel-3);--bs-table-color:var(--ink-2)}
.table>thead th{font-size:.66rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;white-space:nowrap}
.alert{--bs-alert-border-color:transparent;font-size:.8125rem;border-radius:var(--r);display:flex;gap:10px;align-items:flex-start}
.alert svg{width:15px;height:15px;flex:none;margin-top:2px}
.alert-success{--bs-alert-color:var(--ok);--bs-alert-bg:var(--ok-50);--bs-alert-border-color:color-mix(in srgb,var(--ok) 30%,transparent)}
.alert-danger{--bs-alert-color:var(--crit);--bs-alert-bg:var(--crit-50);--bs-alert-border-color:color-mix(in srgb,var(--crit) 30%,transparent)}
.alert-info{--bs-alert-color:var(--brand);--bs-alert-bg:var(--brand-50);--bs-alert-border-color:color-mix(in srgb,var(--brand) 26%,transparent)}
.dropdown-menu{--bs-dropdown-bg:var(--panel);--bs-dropdown-border-color:var(--line);--bs-dropdown-link-hover-bg:var(--panel-2);--bs-dropdown-font-size:.8125rem}
.modal-content{background:var(--panel);border-color:var(--line)}
.nav-tabs{--bs-nav-tabs-link-active-color:var(--brand);--bs-nav-tabs-border-color:var(--line)}
.pagination{--bs-pagination-color:var(--ink-2);--bs-pagination-bg:var(--panel);--bs-pagination-border-color:var(--line);--bs-pagination-active-bg:var(--brand);--bs-pagination-active-border-color:var(--brand);--bs-pagination-font-size:.8125rem}

/* ============================ 3. BASE ============================ */
body{-webkit-font-smoothing:antialiased}
.num{font-variant-numeric:tabular-nums}
.mono{font-family:var(--mono)}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
[x-cloak]{display:none!important}

/* ============================ 4. APP SHELL ============================ */
/* grid-template-columns is animatable, so collapsing to the rail slides
   rather than snapping. Degrades to instant where it is not supported. */
.app{display:grid;grid-template-columns:var(--side-w) minmax(0,1fr);min-height:100vh;
     transition:grid-template-columns .2s ease}
@media(prefers-reduced-motion:reduce){.app{transition:none}}
@media(max-width:991.98px){
  .app{grid-template-columns:1fr}
  aside.side{position:fixed;z-index:1045;left:0;top:0;transform:translateX(-100%);transition:transform .2s ease}
  aside.side.open{transform:none;box-shadow:var(--shadow-2)}
  .scrim{position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:1040}
}
@media(min-width:992px){.menutoggle,.scrim{display:none}}

aside.side{background:var(--side);color:var(--side-ink);display:flex;flex-direction:column;position:sticky;top:0;height:100vh;width:var(--side-w)}
.brand{display:flex;align-items:center;gap:10px;padding:16px;border-bottom:1px solid rgba(255,255,255,.07)}
.brand .logo{
  width:32px;height:32px;border-radius:5px;flex:none;position:relative;
  background:linear-gradient(160deg,#2A67C4,#12356F);display:grid;place-items:center;
  font-weight:800;color:#fff;font-size:17px;letter-spacing:-.02em;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.14);
}
.brand .logo::after{content:"";position:absolute;left:0;top:6px;bottom:6px;width:3px;background:var(--red);border-radius:3px 0 0 3px}
.brand .nm{font-weight:700;font-size:12.5px;color:#fff;line-height:1.2}
.brand .sub{font-size:10px;color:var(--side-ink-2);letter-spacing:.1em;text-transform:uppercase;margin-top:2px}

nav.mainnav{padding:12px 10px;display:flex;flex-direction:column;gap:1px;flex:1;overflow-y:auto}
nav.mainnav .grp{font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--side-ink-2);padding:14px 10px 6px;font-weight:600}
nav.mainnav a{
  display:flex;align-items:center;gap:10px;padding:8px 10px;border-radius:5px;text-decoration:none;
  color:var(--side-ink);font-size:13.5px;font-weight:500;border-left:3px solid transparent;
  transition:background .16s ease,color .16s ease;
}
nav.mainnav a:hover{background:rgba(255,255,255,.06);color:#fff}
nav.mainnav a.on{background:var(--side-2);color:#fff;border-left-color:var(--red);font-weight:600}
nav.mainnav a svg{width:16px;height:16px;flex:none;opacity:.9}
nav.mainnav a .navbadge{margin-left:auto;background:var(--red);color:#fff;font-size:10.5px;font-weight:700;padding:1px 6px;border-radius:9px}
nav.mainnav .sub-nav{margin-left:12px;display:flex;flex-direction:column;gap:1px}

/* ---- expandable groups ---- */
.navgroup{position:relative}
nav.mainnav .grouphead{
  display:flex;align-items:center;gap:10px;width:100%;padding:8px 10px;border-radius:5px;
  background:transparent;border:0;border-left:3px solid transparent;
  color:var(--side-ink);font-size:13.5px;font-weight:500;text-align:left;cursor:pointer;
  transition:background .16s ease,color .16s ease;
}
nav.mainnav .grouphead:hover{background:rgba(255,255,255,.06);color:#fff}
nav.mainnav .grouphead.on{color:#fff;font-weight:600}
nav.mainnav .grouphead svg.ico{width:16px;height:16px;flex:none;opacity:.9}
nav.mainnav .grouphead .chev{
  margin-left:auto;width:13px;height:13px;flex:none;opacity:.65;
  transition:transform .18s ease;
}
nav.mainnav .grouphead[aria-expanded="true"] .chev{transform:rotate(90deg)}
nav.mainnav a .lbl{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ---- rail mode (desktop only) ----
   Below 992px the sidebar is already an off-canvas drawer, where a 64px rail
   would be worse than useless. */
@media(min-width:992px){
  html[data-sidebar="rail"]{--side-w:64px}
  html[data-sidebar="rail"] .brand{justify-content:center;padding:16px 0}
  html[data-sidebar="rail"] .brand>div:last-child,
  html[data-sidebar="rail"] nav.mainnav .lbl,
  html[data-sidebar="rail"] nav.mainnav .grouphead .chev,
  html[data-sidebar="rail"] nav.mainnav>.grp,
  html[data-sidebar="rail"] .sidefoot{display:none}
  html[data-sidebar="rail"] nav.mainnav{padding:12px 8px}
  html[data-sidebar="rail"] nav.mainnav a,
  html[data-sidebar="rail"] nav.mainnav .grouphead{justify-content:center;padding:9px 0;border-left-width:0}
  html[data-sidebar="rail"] nav.mainnav a.on{border-left-width:0;box-shadow:inset 3px 0 0 var(--red)}

  /* Children have nowhere to nest at 64px, so they become a hover flyout.
     !important beats the inline display:none Alpine's x-show writes — in rail
     mode the group's own open/closed state is irrelevant. */
  html[data-sidebar="rail"] .navgroup>.sub-nav{
    display:none!important;position:absolute;left:100%;top:0;margin-left:0;
    min-width:196px;padding:6px;z-index:1050;
    background:var(--side-2);border-radius:0 6px 6px 0;box-shadow:var(--shadow-2);
  }
  html[data-sidebar="rail"] .navgroup:hover>.sub-nav,
  html[data-sidebar="rail"] .navgroup:focus-within>.sub-nav{display:flex!important}
  html[data-sidebar="rail"] .navgroup>.sub-nav .lbl{display:inline}
  html[data-sidebar="rail"] .navgroup>.sub-nav a{justify-content:flex-start;padding:7px 10px}
}
@media(max-width:991.98px){#railBtn{display:none}}
.sidefoot{padding:12px 16px;border-top:1px solid rgba(255,255,255,.07);font-size:11px;color:var(--side-ink-2)}

.top{position:sticky;top:0;z-index:1020;background:var(--panel);border-bottom:1px solid var(--line);display:flex;align-items:center;gap:14px;padding:0 16px;height:56px}
.crumb{display:flex;align-items:center;gap:7px;font-size:13px;color:var(--muted);min-width:0}
.crumb a{color:var(--muted);text-decoration:none}
.crumb a:hover{color:var(--brand)}
.crumb .cur{color:var(--ink);font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.crumb svg{width:13px;height:13px;opacity:.5;flex:none}
.iconbtn{width:33px;height:33px;border-radius:5px;border:1px solid var(--line);background:var(--panel);display:grid;place-items:center;cursor:pointer;color:var(--ink-2);position:relative;transition:background .16s,border-color .16s,color .16s}
.iconbtn:hover{background:var(--panel-2);color:var(--brand);border-color:var(--line-2)}
.iconbtn svg{width:16px;height:16px}
.iconbtn .dot{position:absolute;top:6px;right:6px;width:6px;height:6px;border-radius:50%;background:var(--red)}
.avatar{width:31px;height:31px;border-radius:50%;background:var(--brand);color:#fff;display:grid;place-items:center;font-size:11.5px;font-weight:700;flex:none}
html[data-theme="dark"] .avatar{color:#0B1622}
.who{display:flex;align-items:center;gap:9px}
.who .n{font-size:12.5px;font-weight:600;line-height:1.15}
.who .r{font-size:11px;color:var(--muted);line-height:1.15}

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

/* No max-width: every screen here is a table or a form that constrains itself,
   and capping the page meant collapsing the sidebar just moved 172px of
   whitespace to the right instead of giving it to the content. */
.page{padding:16px;display:flex;flex-direction:column;gap:16px;width:100%;min-width:0}
@media(min-width:768px){.page{padding:20px}}

/* Opt-in for anything that is genuinely prose and would be unreadable at
   1900px - a long note, a policy page. Not for tables. */
.page.reading{max-width:76ch}

/* ============ 5. COMPONENTS BOOTSTRAP LACKS ============ */
.panel{background:var(--panel);border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--shadow-1);overflow:hidden}
.phead{display:flex;align-items:center;gap:10px;padding:12px 16px;border-bottom:1px solid var(--line);background:var(--panel-2);flex-wrap:wrap}
.phead h2{margin:0;font-size:13.5px;font-weight:700}
.phead .hint{font-size:12px;color:var(--muted)}
.phead .sp{flex:1}
/* The include switch, beside the sheet title and centred on it.
   A Bootstrap form-check brings a 1.5rem min-height, a bottom margin and a
   top-aligned input, all of which make it ride high against a 13.5px heading;
   all three are undone here and the row is centred instead. The extra left
   margin keeps it off the title - it is an aside, not part of the name. */
#rExclude{display:flex;align-items:center;margin-left:8px}
#rExclude .form-check{display:flex;align-items:center;gap:8px;
  min-height:0;margin:0;padding:0}
#rExclude .form-check-input{float:none;margin:0;flex-shrink:0}
#rExclude .form-check-label{font-size:12px;line-height:1;color:var(--muted)}
.pbody{padding:16px}

/* =========================================================================
   Loading bar - Api.request() puts it up while calls are in flight.

   Fixed to the top of the window, above everything, and click-through, so it
   never takes a pointer event from the screen underneath. Api.js creates the
   element on first use; no page has to declare it.
   ========================================================================= */
#apiProgress{position:fixed;top:0;left:0;right:0;height:3px;z-index:2000;
  overflow:hidden;pointer-events:none;background:var(--brand-50);
  opacity:0;transition:opacity .18s ease}
#apiProgress.on{opacity:1}
#apiProgress::before{content:"";position:absolute;top:0;bottom:0;left:-40%;width:40%;
  border-radius:3px;background:var(--brand);animation:apiSlide 1s ease-in-out infinite}
@keyframes apiSlide{
  0%{left:-40%;width:40%}
  55%{width:58%}
  100%{left:100%;width:40%}
}

/* Some people are made unwell by motion; the bar still says "busy", it just
   stops travelling. */
@media (prefers-reduced-motion:reduce){
  #apiProgress::before{animation:none;left:0;width:100%;opacity:.55}
}

.pill{display:inline-flex;align-items:center;gap:5px;font-size:11px;font-weight:600;letter-spacing:.03em;padding:2px 8px;border-radius:20px;border:1px solid;white-space:nowrap}
.pill.blue{color:var(--brand);background:var(--brand-50);border-color:color-mix(in srgb,var(--brand) 26%,transparent)}
.pill.ok{color:var(--ok);background:var(--ok-50);border-color:color-mix(in srgb,var(--ok) 30%,transparent)}
.pill.warn{color:var(--warn);background:var(--warn-50);border-color:color-mix(in srgb,var(--warn) 30%,transparent)}
.pill.crit{color:var(--crit);background:var(--crit-50);border-color:color-mix(in srgb,var(--crit) 30%,transparent)}
.pill.grey{color:var(--muted);background:var(--panel-2);border-color:var(--line)}
.pill.dark{color:#fff;background:var(--side);border-color:var(--side)}

.callout{border-left:3px solid var(--brand);background:var(--brand-50);padding:11px 13px;border-radius:0 var(--r) var(--r) 0;font-size:12.5px;line-height:1.5;color:var(--ink-2)}
.callout b{color:var(--ink)}
.callout.amber{border-left-color:var(--warn);background:var(--warn-50)}
.notebar{display:flex;gap:10px;align-items:flex-start;background:var(--panel);border:1px dashed var(--line-2);border-radius:var(--r);padding:11px 14px;font-size:12.5px;color:var(--ink-2);line-height:1.5}
.notebar svg{width:15px;height:15px;color:var(--brand);flex:none;margin-top:2px}
.notebar b{color:var(--ink)}

.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));border-top:1px solid var(--line);background:var(--panel-2)}
.kpis.standalone{border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--shadow-1);overflow:hidden}
.kpi{padding:12px 16px;border-right:1px solid var(--line);border-bottom:1px solid var(--line)}
.kpi .lb{font-size:10.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--muted);font-weight:600;margin-bottom:3px}
.kpi .vl{font-size:18px;font-weight:700;letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.kpi .vl small{font-size:11px;font-weight:600;color:var(--muted);margin-left:3px}
.kpi.accent .vl{color:var(--ok)}

.mini{display:flex;flex-direction:column}
.mini .line{display:flex;justify-content:space-between;gap:12px;padding:7px 0;border-bottom:1px dashed var(--line);font-size:13px}
.mini .line:last-child{border-bottom:0}
.mini .line .k{color:var(--ink-2)}
.mini .line .v{font-weight:600;font-variant-numeric:tabular-nums}
.mini .line.neg .v{color:var(--red)}
.mini .line.fin{border-top:2px solid var(--line-2);border-bottom:0;margin-top:5px;padding-top:9px}
.mini .line.fin .k{font-weight:700;color:var(--ink)}
.mini .line.fin .v{font-weight:800;font-size:15px;color:var(--brand)}

.tl2{display:flex;flex-direction:column}
.tl2 .ev{display:grid;grid-template-columns:20px 1fr;gap:10px;padding:0 0 14px;position:relative}
.tl2 .ev:last-child{padding-bottom:0}
.tl2 .ev::before{content:"";position:absolute;left:9px;top:19px;bottom:0;width:1px;background:var(--line)}
.tl2 .ev:last-child::before{display:none}
.tl2 .dot{width:19px;height:19px;border-radius:50%;display:grid;place-items:center;background:var(--panel-2);border:1px solid var(--line-2);z-index:1}
.tl2 .dot svg{width:11px;height:11px;color:var(--muted)}
.tl2 .ev.ok .dot{background:var(--ok-50);border-color:var(--ok)}
.tl2 .ev.ok .dot svg{color:var(--ok)}
.tl2 .tx{font-size:12.5px;line-height:1.4}
.tl2 .tm{font-size:11px;color:var(--muted);font-family:var(--mono);margin-top:1px}

/* activity feed — the project history, on the project panel and its own page */
.feed{display:flex;flex-direction:column}
/* dot | who + when | what changed. Three columns rather than one stacked
   block: the name and the time are the same two short strings on every row, so
   giving them a fixed lane lets the eye run down it, and the panel loses about
   a third of its height. */
.feed .ev{display:grid;grid-template-columns:9px 120px 1fr;gap:10px;padding:9px 16px;border-bottom:1px solid var(--line);align-items:start}
/* .actor, NOT .who - the topbar already owns .who and it is display:flex, which
   put the name and the time side by side and squeezed the name to "Irfa...".
   Check a new class against this file as well as against Bootstrap. */
.feed .actor{min-width:0}
.feed .actor b{display:block;font-size:12.5px;color:var(--ink);line-height:1.25}
.feed .actor .tm{display:block}

@media (max-width:575.98px){
  /* No room for a lane this narrow on a phone - fall back to stacked. */
  .feed .ev{grid-template-columns:9px 1fr}
  .feed .actor{margin-bottom:2px}
}
.feed .ev:last-child{border-bottom:0}
.feed .dot{width:8px;height:8px;border-radius:50%;background:var(--line-2);margin-top:6px}
.feed .ev.created .dot{background:var(--ok)}
.feed .ev.updated .dot{background:var(--brand)}
.feed .ev.deleted .dot{background:var(--red)}
.feed .tx{font-size:12.5px;line-height:1.45;color:var(--ink-2)}
.feed .tx b{color:var(--ink)}
.feed .tm{font-size:11px;color:var(--muted);font-family:var(--mono);margin-top:1px}
.feed .chg{margin:6px 0 0;padding:0;list-style:none;font-size:12px}
.feed .chg li{padding:1px 0}
.feed .chg .lb{color:var(--muted)}
.feed .chg .fr{color:var(--muted);text-decoration:line-through}
.feed .chg .to{color:var(--ink);font-weight:600}
.feed .empty{padding:26px 16px;text-align:center;color:var(--muted);font-size:12.5px}

/* signature strip — scrolls horizontally on a phone rather than shrinking */
.sigscroll{overflow-x:auto}
.sigstrip{display:grid;grid-template-columns:repeat(9,minmax(112px,1fr));min-width:1010px}
.sigcell{border-right:1px solid var(--line);padding:10px 11px 9px;display:flex;flex-direction:column;gap:5px;min-height:104px}
.sigcell:last-child{border-right:0}
.sigcell.now{background:var(--warn-50)}
.sigcell.skip{background:var(--panel-2)}
.sigcell .seq{width:19px;height:19px;border-radius:50%;display:grid;place-items:center;font-size:10.5px;font-weight:700;font-family:var(--mono);border:1px solid var(--line-2);color:var(--muted);background:var(--panel)}
.sigcell.done .seq{background:var(--ok);border-color:var(--ok);color:#fff}
.sigcell.now .seq{background:var(--warn);border-color:var(--warn);color:#fff}
.sigcell.skip .seq{border-style:dashed}
.sigcell .dept{font-size:10.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-2);line-height:1.25}
.sigcell.done .dept,.sigcell.now .dept{color:var(--ink)}
.sigcell .sign{margin-top:auto;height:22px;display:flex;align-items:flex-end}
.sigcell .sign .sg{font-family:"Segoe Script","Bradley Hand",cursive;font-size:16px;color:var(--brand);opacity:.85;transform:rotate(-3deg)}
.sigcell .who{font-size:11px;color:var(--muted);font-weight:600;border-top:1px solid var(--line);padding-top:4px;display:block}
.sigcell .when{font-size:10px;color:var(--muted);font-family:var(--mono)}
.sigcell .stt{font-size:10px;font-weight:700;letter-spacing:.05em;text-transform:uppercase}
.sigcell.done .stt{color:var(--ok)}
.sigcell.now .stt{color:var(--warn)}
.sigcell.skip .stt,.sigcell.pend .stt{color:var(--muted)}

/* ============================ 6. RAB SHEETS ============================ */
.rabhead{background:var(--panel);border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--shadow-1);overflow:hidden}
.rabhead .hrow{display:flex;flex-wrap:wrap;gap:16px;align-items:flex-start;padding:16px}
.rabhead h1{margin:0 0 5px;font-size:19px;font-weight:700;line-height:1.22}
.rabhead .subline{display:flex;flex-wrap:wrap;gap:6px;align-items:center;font-size:12.5px;color:var(--muted)}
.rabhead .sep{opacity:.45}
.headacts{margin-left:auto;display:flex;gap:8px;flex-wrap:wrap}

.rail{display:flex;overflow-x:auto;background:var(--panel);border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--shadow-1);scrollbar-width:thin}
.rail button{flex:none;min-width:126px;text-align:left;cursor:pointer;background:transparent;border:0;border-right:1px solid var(--line);border-bottom:2px solid transparent;padding:10px 14px 9px;display:flex;flex-direction:column;gap:3px;transition:background .16s ease,border-color .16s ease}
.rail button:last-child{border-right:0}
.rail button:hover{background:var(--panel-2)}
.rail button.on{background:var(--brand-50);border-bottom-color:var(--brand)}
.rail .tl{display:flex;align-items:center;gap:6px;font-size:12.5px;font-weight:600;color:var(--ink-2);white-space:nowrap}
.rail button.on .tl{color:var(--brand)}
.rail .tv{font-size:12.5px;font-weight:700;color:var(--ink);font-variant-numeric:tabular-nums}
.rail .tv.zero{color:var(--muted);font-weight:500}
/* #rAlert and #rDrift sit between the header and the rail and are empty
   almost all the time - but as flex items of .page each still costs a 16px
   gap, which opened a 48px hole above the rail. Empty means not there. */
#rAlert:empty,#rDrift:empty{display:none}
/* The rail is a row of buttons a keyboard can reach - keep the focus ring. */
.rail button:focus-visible{outline:2px solid var(--brand);outline-offset:-2px}
.st{width:7px;height:7px;border-radius:50%;flex:none;background:var(--line-2)}
.st.approved{background:var(--ok)}
.st.waiting{background:var(--warn);box-shadow:0 0 0 3px color-mix(in srgb,var(--warn) 18%,transparent)}
/* Red, because "tidak termasuk" is the one state the reader must not mistake
   for "not filled in yet" - a grey dot beside a dash looks like work still to
   do, and this sheet is deliberately out of the job. */
.st.excluded{background:var(--red)}

/* recap table — scoped so stock .table is untouched */
table.recap{border-collapse:separate;border-spacing:0;width:100%;font-size:13px}
table.recap th,table.recap td{padding:8px 14px;border-bottom:1px solid var(--line);white-space:nowrap;text-align:left}
table.recap thead th{position:sticky;top:0;z-index:5;background:var(--panel-3);color:var(--ink-2);font-size:10.5px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;border-bottom:1px solid var(--line-2)}
table.recap td.r,table.recap th.r{text-align:right;font-variant-numeric:tabular-nums}
table.recap .grp td{background:var(--panel-3);font-weight:700;font-size:11.5px;letter-spacing:.07em;text-transform:uppercase;color:var(--ink)}
table.recap .idx{color:var(--muted);font-family:var(--mono);font-size:11.5px;width:44px}
table.recap .subtot td{background:var(--panel-2);font-weight:700}
table.recap .grand td{background:var(--brand-50);font-weight:800;color:var(--brand);font-size:13.5px}
table.recap .contrib td{background:var(--ok-50);font-weight:800;color:var(--ok);font-size:13.5px}
/* The greyed negotiation block on the right of page 2. Quieter than the
   contract block beside it because no total on the page derives from it — it
   records how the price was reached, which is a note, not an input. */
/* Page 2, laid out as the prototype: recap left, a 330px column of Nilai
   Kontrak and Aktivitas right, the signatures full width underneath. The
   [hidden] rule is needed because display:flex would otherwise beat the
   hidden attribute and the pane would never go away. */
.recapview{display:flex;flex-direction:column;gap:16px}
.recapview[hidden]{display:none}
.split{display:grid;grid-template-columns:minmax(0,1fr) 330px;gap:16px;align-items:start}
@media(max-width:1120px){.split{grid-template-columns:1fr}}
.recapside{display:flex;flex-direction:column;gap:16px}
.mini .line .v.nil{color:var(--muted);font-weight:500}
.mini.muted .line .k,.mini.muted .line .v{color:var(--muted);font-weight:600}
.mini.muted .line .v{font-weight:700}
table.recap .live{color:var(--brand);font-weight:700}
table.recap .src{font-size:11px;color:var(--muted);font-weight:400;margin-left:7px}
table.recap .colgrp{text-align:center;border-left:1px solid var(--line-2);color:var(--muted)}
table.recap td.kor{border-left:1px solid var(--line);color:var(--muted);text-align:right}

/* Which sheet is open, readable at a glance from eight tabs of near-identical
   length. Bootstrap marks the active tab with a border alone, which is too
   quiet next to seven siblings that all start with the same kind of word. */
#rTabs .nav-link{font-weight:500}
#rTabs .nav-link.active{font-weight:700;color:var(--brand)}
/* The approval mark beside a tab name: tick approved, dot being signed, dash
   not used. Colour and shape both carry it, so it reads in greyscale too. */
#rTabs .tabmark{font-size:11px;margin-left:3px}
#rTabs .tabmark.ok{color:var(--ok)}
#rTabs .tabmark.warn{color:var(--warn)}
#rTabs .tabmark.muted{color:var(--muted)}

/* spreadsheet grid */
.grid-wrap{overflow:auto;max-height:560px;border-bottom:1px solid var(--line)}
table.gridsheet{border-collapse:separate;border-spacing:0;width:100%;font-size:12.5px}
table.gridsheet th,table.gridsheet td{padding:0;border-right:1px solid var(--line);border-bottom:1px solid var(--line);white-space:nowrap;text-align:left}
/* line-height is pinned so the sticky offset below is a number and not a guess:
   14 + 7 + 7 + 1px border = 29px, whatever font the browser falls back to. */
table.gridsheet th{padding:7px 9px;line-height:14px;position:sticky;top:0;z-index:5;background:var(--panel-3);color:var(--ink-2);font-size:10.5px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;border-bottom:1px solid var(--line-2)}
table.gridsheet th.r{text-align:right}
/* The second header row - the columns under JUMLAH, HARGA SATUAN, TOTAL HARGA.
   It sticks one header-row down rather than at 0, or it would sit on top of the
   groups it belongs to as soon as the sheet is scrolled. */
table.gridsheet thead tr:nth-child(2) th{top:29px}
/* A group heading spanning the columns it covers. Centred and bordered off, so
   the eye reads "these five are Harga Satuan" instead of five loose words. */
table.gridsheet th.colgrp{text-align:center;color:var(--ink);border-left:1px solid var(--line-2);border-right:1px solid var(--line-2)}
table.gridsheet tbody tr:hover{background:var(--panel-2)}
table.gridsheet td.calc{background:var(--panel-2);color:var(--ink-2);font-weight:600;text-align:right;font-variant-numeric:tabular-nums;padding:5px 9px}
table.gridsheet td.calc.tot{color:var(--ink);font-weight:700}
table.gridsheet input{width:100%;border:0;background:transparent;padding:5px 9px;font-size:12.5px;border-radius:0;transition:background .13s ease,box-shadow .13s ease}
table.gridsheet input:hover{background:var(--panel-3)}
/* No spin buttons. Chrome reserves ~13px at the right edge of every
   input[type=number] for them, so a right-aligned Qty sat 13px to the left of
   the read-only Total beside it and of the subtotal underneath it - the column
   of figures looked out by exactly the width of a control nobody uses on a
   sheet this size. Arrow keys still step the value. */
table.gridsheet input[type=number]{-moz-appearance:textfield;appearance:textfield}
table.gridsheet input[type=number]::-webkit-outer-spin-button,
table.gridsheet input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;appearance:none;margin:0}
table.gridsheet input:focus{outline:0;background:var(--brand-50);box-shadow:inset 0 0 0 1.5px var(--brand)}
table.gridsheet input.r{text-align:right;font-variant-numeric:tabular-nums}
table.gridsheet input.mono{font-family:var(--mono);font-size:11.5px}
.stickL{position:sticky;left:0;z-index:4;background:var(--panel);border-right:1px solid var(--line-2)!important}
thead .stickL{z-index:6;background:var(--panel-3)}
tbody tr:hover .stickL{background:var(--panel-2)}
.stickL2{position:sticky;left:44px;z-index:4;background:var(--panel);border-right:1px solid var(--line-2)!important;min-width:190px}
thead .stickL2{z-index:6;background:var(--panel-3)}
tbody tr:hover .stickL2{background:var(--panel-2)}
.rowno{color:var(--muted);font-family:var(--mono);font-size:11px;text-align:right;padding:5px 9px;display:block;width:44px}
.rowact{display:flex;gap:2px;padding:3px 6px}
.rowact button{width:22px;height:22px;border:0;background:transparent;border-radius:4px;color:var(--muted);cursor:pointer;display:grid;place-items:center}
.rowact button:hover{background:var(--panel-3);color:var(--brand)}
.rowact button.del:hover{background:var(--red-50);color:var(--red)}
.rowact svg{width:13px;height:13px}
/* sprint 1.4 sheet grid — a cell holding text rather than an input carries the
   padding the input would have had. An explicit class rather than :has(), so
   the grid does not depend on a selector an older browser may not support. */
table.gridsheet td.pad{padding:5px 9px}
/* Column widths, by the col-<name> class rab-sheet.js puts on every cell.
   Text columns get room for what they actually hold - a Hardware Type/Ukuran
   runs to fifty characters - and counts get only what a count needs, so the
   sheet is not half empty space beside a truncated description. The full text
   of a long cell is also on hover (TOOLTIP_COLUMNS in rab-sheet.js). */
table.gridsheet .col-group_name{min-width:210px}
table.gridsheet .col-item_name{min-width:190px}
table.gridsheet .col-item_type{min-width:340px}
table.gridsheet .col-notes{min-width:160px}
table.gridsheet .col-qty,
table.gridsheet .col-qty_sheets,
table.gridsheet .col-qty_openings,
table.gridsheet .col-procurement_qty,
table.gridsheet .col-qty_door,
table.gridsheet .col-qty_door_window,
table.gridsheet .col-qty_window,
table.gridsheet .col-qty_partition{width:76px;min-width:64px}
table.gridsheet .col-unit_code{width:60px}
/* Page 10's Bobot %: written only on the subtotal rows. */
table.gridsheet .col-bobot{width:74px}
/* The upah sheets (pages 8 and 9) have one text column and it carries the
   whole description - "Pasang Frame SF 3"/ 4" + Acc + Cleaning". 190px would
   truncate every row, so those two pages widen it; the material sheets, which
   split the description across Nama Barang and Type/Ukuran, are left alone. */
table.gridsheet.upah .col-item_name{min-width:340px}
/* The suggested quantity, under the Qty field it belongs to. See
   RabQuantityHintService: it is offered, never applied, so it has to read as a
   control somebody presses and not as a second value in the cell. Sized and
   coloured down to stay quieter than the figure above it. */
.qtyhint{display:block;width:100%;margin-top:2px;padding:0;border:0;background:none;font-size:11px;line-height:1.3;text-align:right;color:var(--brand);font-variant-numeric:tabular-nums;cursor:pointer}
.qtyhint::before{content:"≈ "}
.qtyhint:hover,.qtyhint:focus-visible{text-decoration:underline}
/* Page 10 foots each block as well as the page. Lighter than the sticky page
   footer and heavier than a data row, so the eye reads three levels down the
   sheet — heading, rows, block total — and still finds the real total at the
   bottom. Only Overhead asks for these; see SheetType::showsGroupSubtotals(). */
table.gridsheet tr.subtotalrow td{background:var(--panel-2);border-top:1px solid var(--line-2);font-weight:600;font-variant-numeric:tabular-nums}
table.gridsheet tr.subtotalrow td.bobot{color:var(--muted);font-weight:500}
table.gridsheet tfoot th.bobot{color:var(--brand)}
/* 9px horizontal, the same as td.pad and the inputs. An 11px gutter here put
   every right-aligned subtotal two pixels inside the figure above it - small,
   and the kind of small that makes a column of numbers look untrustworthy. */
table.gridsheet tfoot th{position:sticky;bottom:0;background:var(--panel-3);border-top:1px solid var(--line-2);border-bottom:0;font-weight:700;padding:9px;text-align:left;text-transform:none;letter-spacing:0;font-size:12.5px;color:var(--ink)}
table.gridsheet tfoot th.text-end{text-align:right}
table.gridsheet tfoot td{position:sticky;bottom:0;background:var(--panel-3);border-top:1px solid var(--line-2);border-bottom:0;font-weight:700;padding:9px 11px}
table.gridsheet tfoot td.r{text-align:right;font-variant-numeric:tabular-nums}
table.gridsheet tfoot .gt{color:var(--brand);font-size:13.5px}
/* The group heading - "Hardware Pintu Swing Alm" - printed whenever the group
   changes down the sheet, exactly as page 5 does it. Numbering restarts
   underneath it; that part is drawn by rab-sheet.js. */
table.gridsheet tr.grouprow td{background:var(--panel-3);color:var(--ink);font-weight:700;font-size:11px;letter-spacing:.07em;text-transform:uppercase;padding:7px 9px;border-right:0}
/* A line the estimator struck out. It keeps its prices and its place in the
   numbering and contributes nothing - the paper prints the dropped items so the
   client can see what was considered. The database agrees: rab_lines.amount is
   generated as 0 when is_cancelled, so nothing downstream has to remember. */
table.gridsheet tr.linecancel td{color:var(--muted);text-decoration:line-through}
table.gridsheet tr.linecancel input:not([type=checkbox]){color:var(--muted);text-decoration:line-through}
table.gridsheet tr.linecancel:hover td{background:var(--panel-2)}
/* Pintu Kayu's foot-of-page discount, previewed live beside the percentage:
   subtotal, minus the cut, equals what the RAB grand total takes. The net is
   the figure that matters, so it is the one in brand colour. */
#rDiscountSummary .net{color:var(--brand)}
/* "Setelah Diskon" under the sheet total - the figure the recap takes. */
table.gridsheet tfoot .net{color:var(--brand)}
.gridfoot{display:flex;gap:8px;align-items:center;padding:10px 14px;flex-wrap:wrap;background:var(--panel)}
.gridfoot .kbd{font-family:var(--mono);font-size:10.5px;padding:1px 5px;border-radius:3px;border:1px solid var(--line-2);background:var(--panel-2);color:var(--ink-2)}
.gridfoot .tip{font-size:11.5px;color:var(--muted);margin-left:auto}
@media(max-width:800px){.gridfoot .tip{margin-left:0;width:100%}}

@keyframes flash{0%{background:color-mix(in srgb,var(--brand) 24%,transparent)}100%{background:transparent}}
.flash{animation:flash .5s ease-out}
@keyframes rowin{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}
.rowin{animation:rowin .18s ease-out}
.view{display:none}
.view.on{display:flex;flex-direction:column;gap:16px}

/* ============================ 7. LOGIN ============================ */
.authwrap{min-height:100vh;display:grid;grid-template-columns:1fr 460px}
@media(max-width:991.98px){.authwrap{grid-template-columns:1fr}.authside{display:none}}
.authside{background:var(--side);color:var(--side-ink);padding:44px 48px;display:flex;flex-direction:column;justify-content:space-between;position:relative;overflow:hidden}
.authside::after{content:"";position:absolute;right:-180px;bottom:-180px;width:520px;height:520px;border-radius:50%;background:radial-gradient(circle at 30% 30%,rgba(42,103,196,.42),transparent 66%)}
.authside .brand{border-bottom:0;padding:0}
.authside h1{font-size:29px;line-height:1.22;font-weight:700;color:#fff;margin:0 0 14px;letter-spacing:-.02em;max-width:15ch}
.authside p{font-size:14px;line-height:1.6;color:var(--side-ink);margin:0;max-width:46ch}
.authside .mid{position:relative;z-index:1}
.authside .feat{display:flex;flex-direction:column;gap:9px;margin-top:22px}
.authside .feat div{display:flex;gap:9px;align-items:center;font-size:13px}
.authside .feat svg{width:15px;height:15px;color:#6FA3EE;flex:none}
.authside .foot{font-size:11.5px;color:var(--side-ink-2);position:relative;z-index:1}
.authmain{display:grid;place-items:center;padding:24px;background:var(--panel)}
.authcard{width:100%;max-width:340px}
.authcard .mobbrand{display:none}
@media(max-width:991.98px){.authcard .mobbrand{display:flex;margin-bottom:26px}}
.authcard h2{font-size:21px;font-weight:700;margin:0 0 5px}
.authcard .lede{font-size:13.5px;color:var(--muted);margin:0 0 22px}
.authcard .foot{margin-top:26px;font-size:11.5px;color:var(--muted);text-align:center;line-height:1.6}

@media(prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}

/* -------------------------------------------------------------------------
   Row actions

   Table rows carry up to three buttons - Ubah, Reset Sandi, Nonaktifkan - and
   inline-block buttons with margins wrap onto a second line as soon as the
   column is a few pixels short, which reads as overlapping. A flex row that
   refuses to wrap keeps them on one line and lets the table-responsive
   wrapper scroll instead, which is the honest failure mode.

   Defined once here rather than repeated as utility classes in five different
   renderers, so the spacing is tuned in one place.
   ------------------------------------------------------------------------- */
.row-actions{display:flex;gap:.35rem;justify-content:flex-end;flex-wrap:nowrap}
.row-actions .btn{white-space:nowrap}
