/* Seeds the real stylesheet. Brand colour sampled from the logo: #623627
     is 84.6% of its opaque pixels, and it reads 10.11:1 on white (AAA). */
  :root {
    /* Swiss-influenced reskin (2026-09-09): the logo brown stays the sole
       accent; every neutral moved off the warm brown-grey it was mixed from
       onto a true neutral grey, so the accent reads as a deliberate choice
       rather than the warm end of one ramp. Radius is 0 — cards are defined by
       a hairline and whitespace, not rounding. See docs/design-swiss.md. */
    --brand:        #623627;
    --brand-dark:   #4b291e;
    --ink:          #1a1a1a;
    --muted:        #6b6b6b;
    --line:         #d7d7d7;
    --line-soft:    #ebebeb;   /* lighter --line, for within-group column rules */
    --field:        #fff;
    --bg:           #fff; /* pure white canvas (2026-09-16, on request) — cards now separate purely by the var(--line) hairline, no grey backdrop */
    --radius:       0;
    --tap:          44px;      /* minimum touch target, report §7.10 */
    --z:            1;         /* page scale — see the zoom rule below */
    /* One grotesque, in the Swiss manner. Not self-hosted (deliberate, per the
       no-extra-asset rule the login page already follows): Helvetica where the
       OS has it, Arial/Liberation as the metric-compatible fallback, the UI
       sans last. */
    --font:  "Helvetica Neue", Helvetica, Arial, "Liberation Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  }

  /* Every length in this sheet is px, so nothing gave way as the window got
     narrower: a five-outlet Laba Rugi is 1592px of columns and did not fit the
     content column on a 1520px screen, and the report had to be read at browser
     zoom 80%. `zoom` moves that into the stylesheet — layout runs at
     viewport / --z CSS px (1900 on that screen), then paints scaled back down,
     which is what browser zoom was already doing.
     ponytail: one knob beats retuning ~30 px values, and it keeps the table's
     column widths honest — they are still the widths the <colgroup> says.
     Touch opts back out, and it is the second query that does it, not a width —
     scaling by 0.8 also scales what a finger has to hit (--tap lands at 35px)
     and what iOS measures before it zooms a focused input (16px lands at
     12.8px). Both are touch constraints, so a touch pointer answers them,
     rather than a guess about which widths are tablets. Written as an opt-out
     and not as `and (pointer: fine)` deliberately: a device that reports no
     pointer at all then still gets the scale, where requiring `fine` would
     silently drop it.
     Viewport units are NOT rescaled by zoom (verified: 100vh inside zoom .8
     paints 0.8 × the screen), so every vh below divides by --z. */
  @media (min-width: 861px) { :root { --z: 0.8; } }
  @media (pointer: coarse)  { :root { --z: 1; } }
  body { zoom: var(--z); }

  * { box-sizing: border-box; }

  /* Scrollbar chrome hidden everywhere, scrolling itself untouched: wheel,
     touch, and keyboard all still work, and no layout shifts when a box goes
     from fitting to not fitting. Firefox/Chromium 121+ take the standard
     property; the -webkit rule covers older Chromium and Safari.
     ponytail: one global rule beats per-container rules on .nav, .side and
     .table-scroll. If some box later needs its bar back, override there. */
  * { scrollbar-width: none; }
  *::-webkit-scrollbar { display: none; }

  /* .table-scroll IS that later box (see its own rule): once it got a bounded
     height so its sticky caption/header could freeze, it became a real
     internally-scrolling region with ~3500px of body rows a reader cannot
     see all at once. A hidden scrollbar there isn't tidy, it's a dead end —
     the box sits inside an otherwise-short page, so the ordinary page scroll
     runs out almost immediately (chart + ~10 rows + the "Sumber" footer is
     all that fits before the page itself has nowhere further to go), and
     with no visible thumb nothing on screen says "more rows live inside this
     box, scroll *here*." Restoring a thin bar is the one visible cue for it. */
  .table-scroll { scrollbar-width: thin; }
  .table-scroll::-webkit-scrollbar { display: block; width: 10px; height: 10px; }
  .table-scroll::-webkit-scrollbar-thumb { background: var(--line); border-radius: 999px; }
  .table-scroll::-webkit-scrollbar-track { background: transparent; }

  /* Same reasoning for the modal tables: several run wider than their grid
     track and .modal-table-wrap scrolls them — with the bar hidden the overflow
     just looked clipped. A thin horizontal bar is the cue. */
  .modal-table-wrap { scrollbar-width: thin; }
  .modal-table-wrap::-webkit-scrollbar { display: block; height: 8px; }
  .modal-table-wrap::-webkit-scrollbar-thumb { background: var(--line); }
  .modal-table-wrap::-webkit-scrollbar-track { background: transparent; }

  body {
    margin: 0;
    min-height: calc(100vh / var(--z));
    background: var(--bg);
    color: var(--ink);
    font: 16px/1.5 var(--font);
    /* 16px minimum: iOS Safari zooms the page on focus for anything smaller. */
    /* Every screen in this app is a data surface — lock the figures to a grid
       so columns of numbers line up digit for digit. */
    font-variant-numeric: tabular-nums;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
  }
  ::selection { background: color-mix(in srgb, var(--brand) 22%, transparent); }

  .card {
    width: 100%;
    max-width: 420px;
    background: #fff;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 32px 24px 28px;
  }

  /* The logo sits above the form, per instruction. Sized by width so the
     wordmark stays legible at 360px and does not dominate on a desktop. */
  .logo {
    display: block;
    width: 100%;
    max-width: 260px;
    height: auto;
    margin: 0 auto 28px;
  }

  h1 {
    margin: 0 0 4px;
    font-size: 20px;
    font-weight: 650;
    letter-spacing: -0.01em;
  }
  /* The dashboard H1 is the page's own name and the loudest single word on it.
     Push it: stronger weight, tighter tracking, larger size. The product's
     title deserves more than a body heading — a meeting screen is named by its
     title, and a 20px generic H1 reads as "admin page," not as "the screen we
     agreed to look at together." Only the dashboard's H1 — no other page in
     the app is a monitor surface, so no other H1 gets this treatment. */
  .dash h1 {
    font-size: 30px;
    font-weight: 700;
    letter-spacing: -0.025em;
    line-height: 1.05;
  }

  .sub { margin: 0 0 24px; color: var(--muted); font-size: 14px; }

  label {
    display: block;
    margin-bottom: 6px;
    font-size: 14px;
    font-weight: 600;
  }

  input {
    width: 100%;
    min-height: var(--tap);
    padding: 10px 12px;
    margin-bottom: 18px;
    font-size: 16px;
    font-family: inherit;
    color: var(--ink);
    background: var(--field);
    border: 1px solid var(--line);
    border-radius: 8px;
  }

  /* Visible focus is not optional — outlet phones get used in bad light. */
  input:focus-visible, button:focus-visible, a:focus-visible, summary:focus-visible {
    outline: 2px solid var(--brand);
    outline-offset: 2px;
  }

  .hint { margin: -12px 0 18px; font-size: 13px; color: var(--muted); }

  button {
    width: 100%;
    min-height: var(--tap);
    font-size: 16px;
    font-weight: 650;
    font-family: inherit;
    color: #fff;
    background: var(--brand);
    border: 0;
    border-radius: 8px;
    cursor: pointer;
  }
  button:hover { background: var(--brand-dark); }

  .reset {
    margin: 20px 0 0;
    padding-top: 18px;
    border-top: 1px solid var(--line);
    font-size: 14px;
    color: var(--muted);
    text-align: center;
  }
  .reset a { color: var(--brand); }

  footer {
    margin-top: 20px;
    font-size: 12px;
    color: var(--muted);
    text-align: center;
  }

  /* No dark theme, deliberately. Report §7.13: projectors and printed handouts
     both favour light, and a second theme is a second thing to keep correct.
     A dark logo on a dark background also needs a second logo asset. */
  :root { color-scheme: light; }


/* --- Shell (layout.html) ------------------------------------------------- */

body.centred {
  min-height: calc(100vh / var(--z));
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px 16px;
}

/* Sidebar left, page right. Grid rather than a fixed-position aside: the
   content column then never has to know the sidebar's width. */
.shell {
  min-height: calc(100vh / var(--z));
  display: grid;
  grid-template-columns: 244px minmax(0, 1fr);
}

/* Fixed to the viewport height, so on a short window the nav scrolls inside it
   rather than spilling its rows out past the white panel. */
.side {
  position: sticky;
  top: 0;
  align-self: start;
  height: calc(100vh / var(--z));
  overflow: hidden;
  display: flex;
  flex-direction: column;
  /* Top padding clears the fixed .navtoggle, so the button never lands on the
     wordmark. Centring the logo inside a narrower box would have done it too,
     and would have moved the logo off the sidebar's centre line to do it. */
  padding: 56px 14px 20px;
  background: #fff;
  border-right: 1px solid var(--line);
}
.side .brand {
  display: block;
  padding: 10px 10px 22px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--line);
}
.side .brand img {
  display: block;
  width: 100%;
  max-width: 180px;
  height: auto;
  margin: 0 auto;
}

/* --- Hiding the sidebar --------------------------------------------------- */

/* display:none rather than a width or a transform: the sidebar leaves the
   accessible tree with it, so a screen reader and the tab order agree with what
   is on screen. Nothing animates, because the state is restored on every page
   load and an animation would replay on each one. */
.nav-off .shell { grid-template-columns: minmax(0, 1fr); }
.nav-off .side  { display: none; }
/* With the sidebar gone the button sits over the page's own top-left corner. */
.nav-off .page  { padding-top: 56px; }

/* Fullscreen (F11 or requestFullscreen) no longer hides the sidebar here:
   layout.html folds it to the icon rail instead, so the toggle keeps its tab
   on the rail's border rather than floating over the page's corner. */

.navtoggle {
  position: fixed;
  top: 12px;
  left: 12px;
  z-index: 20;
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  min-height: 0;             /* the global button's 44px min made it 36×44 */
  padding: 0;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 9px;
  color: var(--muted);
  cursor: pointer;
}
.navtoggle:hover { background: var(--bg); color: var(--brand); }
/* ">" opens, "<" closes. Keyed on the root class, not aria-expanded: the class
   is set in <head> before first paint, aria-expanded only after DOMContentLoaded,
   so the arrow never flips on load. */
.navtoggle svg { transform: rotate(180deg); }
.nav-rail .navtoggle svg,
.nav-off .navtoggle svg { transform: none; }
.navtoggle svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
}

.page { max-width: 1100px; margin: 0 auto; padding: 24px 16px; }
/* The archive's filing list is the one page whose content is a wide table rather
   than a column of prose. At .page's 1100px measure nine nowrap columns do not
   fit, so the table ran wider than the page and had to be swiped sideways to be
   read. Lifting the cap gives it the whole column beside the sidebar; every other
   page keeps its readable measure. The prose lines keep one (`ch`), because a
   full-window sentence is its own kind of unreadable. */
.page.page-wide { max-width: none;
  /* No auto margins: a grid item with them stops stretching and sizes to fit its
     content instead, so a table's min-content width (1600px here) pushed <main>
     past the sidebar's column and the whole document scrolled sideways, leaving
     the fixed .navtoggle behind while the sidebar it rides scrolled away. */
  margin: 0; }
.page.page-wide > p { max-width: 90ch; }
.page h1 { font-size: 22px; margin: 0 0 12px; }

/* A page's title and its one primary action, in one row: the button sits over
   the table's own right edge instead of stranded above the search box on the
   left, where a table that runs the width of the page has nothing under it. */
.page-head { display: flex; align-items: center; justify-content: space-between;
  gap: 16px; margin: 0 0 12px; }
.page-head h1 { margin: 0; }

.empty {
  padding: 40px 20px;
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  color: var(--muted);
  text-align: center;
}

.alert, .notice {
  padding: 10px 12px;
  margin: 0 0 18px;
  border-radius: 8px;
  font-size: 14px;
}
.alert  { background: #fbeae6; border: 1px solid #e0b4a8; color: #7a2d18; }
.notice { background: #eef3ee; border: 1px solid #bcd0bd; color: #2f5233; }


/* --- Navigation ---------------------------------------------------------- */

/* The scrolling part: the logo and the account block stay put, the module list
   is what gives way. min-height:0 is what lets a flex child shrink enough to
   scroll at all. */
.nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.nav form { margin: 0; }

.nav a {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: var(--tap);
  padding: 0 12px;
  border-radius: 12px;
  color: var(--muted);
  font-size: 15px;
  font-weight: 600;
  text-decoration: none;
}
.nav a:hover { background: var(--bg); color: var(--ink); }

/* The active module. Brand on white is 10.11:1, so white on brand is too. */
.nav a.on { background: var(--brand); color: #fff; }
.nav a.on:hover { background: var(--brand-dark); color: #fff; }

/* The collapsible group heading. Same row shape as a nav link, but it toggles
   instead of navigating, so it never takes the active pill — that stays on the
   child, and two rows never claim to be the page you are on. */
.navgroup > summary {
  display: flex;
  align-items: center;
  min-height: var(--tap);
  border-radius: 12px;
  cursor: pointer;
  list-style: none;          /* Firefox */
}
.navgroup > summary::-webkit-details-marker { display: none; }
.navgroup > summary:hover { background: var(--bg); }

/* The label fills the row so the toggle area is the chevron. */
.navgroup > summary > a {
  flex: 1;
  min-height: var(--tap);
  padding-right: 4px;
}
.navgroup[open] > summary > a { color: var(--ink); }

/* A group whose section has no landing page of its own (SQ, HRD) labels itself
   with a .grouplabel instead of a link, so it reads the same as the divisions
   that have no tools yet — same word, same weight, just with a chevron. It
   still has to fill the row, and the margin that separates standalone headings
   would push it off-centre inside the summary. */
.navgroup > summary > .grouplabel { flex: 1; margin-top: 0; }

/* A division: a heading over its tools, not a control. Lighter and smaller than
   a nav row so it reads as a label rather than something that failed to click. */
.grouplabel {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 38px;
  margin-top: 10px;
  padding: 0 12px;
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}
.grouplabel svg { opacity: 0.75; }

.navgroup .chev {
  width: 16px;
  height: 16px;
  margin: 0 12px 0 auto;
  color: var(--muted);
  transition: transform .15s;
}
.navgroup[open] .chev { transform: rotate(90deg); }

/* Dashboard views. The rail is what makes them read as children of the row
   above rather than as seven more modules. */
.subnav {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 2px 0 4px 21px;
  padding-left: 9px;
  border-left: 1px solid var(--line);
}
/* 34px, under the 44px touch minimum, is deliberate and desktop-only: the
   sidebar becomes a strip below 860px and the media query puts --tap back. */
.subnav a { min-height: 34px; padding: 0 10px; border-radius: 9px; font-size: 14px; }
/* No tool yet — same row box as a link, just unclickable and muted. */
.subnav-empty {
  display: flex;
  align-items: center;
  min-height: 34px;
  padding: 0 10px;
  font-size: 14px;
  color: var(--muted);
}

/* Stroke icons inherit the row's colour, so the active pill needs no second set. */
.nav svg, .account svg {
  flex: none;
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* --- Account, at the foot of the sidebar --------------------------------- */

/* margin-top:auto pins it to the bottom however short the module list is; the
   negative side margins cancel the sidebar's padding so the block runs edge to
   edge and the sign-out button lands on the sidebar's own right edge. */
.account {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: auto -14px 0;
  padding: 12px 6px 4px 14px;
  border-top: 1px solid var(--line);
}
/* The form is the flex item, not the button inside it — the auto margin has to
   live here or the sign-out sits wherever the NIK happens to end. */
.account form { margin: 0 0 0 auto; }

.account .avatar {
  flex: none;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--bg);
  border: 1px solid var(--line);
  color: var(--brand);
}

/* min-width:0 lets a long NIK ellipsis instead of pushing the button off. */
.account-id { display: grid; min-width: 0; line-height: 1.25; }
.account-id strong,
.account-id small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.account-id strong { font-size: 14px; font-weight: 650; }
.account-id small { font-size: 11px; color: var(--muted); letter-spacing: 0.04em; }

/* Icon-only, so the tap target comes from padding, not from a label. */
.account .signout {
  display: grid;
  place-items: center;
  width: var(--tap);
  height: var(--tap);
  padding: 0;
  background: none;
  border: 0;
  border-radius: 10px;
  color: var(--muted);
  cursor: pointer;
}
.account .signout:hover { background: var(--bg); color: var(--brand); }

/* Under ~860px the sidebar becomes a scrolling strip across the top. Still no
   overlay drawer: sliding a panel over the page needs a focus trap, a scrim and
   an Escape handler, where .nav-off just stops rendering the strip. The toggle
   hides it in both layouts and costs none of that. */
@media (max-width: 860px) {
  .shell { grid-template-columns: minmax(0, 1fr); }
  .side {
    position: static;
    height: auto;
    flex-direction: row;
    align-items: center;
    gap: 8px;
    /* Left padding clears the fixed button here too — in a row the strip starts
       exactly where the button is. */
    padding: 10px 12px 10px 56px;
    overflow: visible;
    overflow-x: auto;
    border-right: 0;
    border-bottom: 1px solid var(--line);
  }
  /* The strip is a row: the logo goes back to a fixed height beside the items. */
  .side .brand {
    flex: none;
    padding: 0 12px 0 4px;
    margin: 0;
    border-bottom: 0;
    border-right: 1px solid var(--line);
  }
  .side .brand img { width: auto; height: 28px; max-width: none; }
  .nav { flex: 0 0 auto; flex-direction: row; align-items: center; gap: 4px; overflow: visible; }
  .nav a, .grouplabel { width: auto; white-space: nowrap; }
  .grouplabel { margin-top: 0; }
  /* In a row the rail and the indent mean nothing, and fingers need the target. */
  .subnav { flex-direction: row; gap: 4px; margin: 0; padding: 0; border-left: 0; }
  .subnav a { min-height: var(--tap); }
  /* The strip is one row, so "bottom" becomes "far end". */
  .account {
    margin: 0 0 0 auto;
    padding: 4px 0 4px 8px;
    border-top: 0;
  }
  .account .avatar { width: 30px; height: 30px; }
  .account-id small { display: none; }
}

/* Desktop: the toggle is a small tab riding the sidebar's right border, half
   in, half out — open (244px) or rail (56px) — so the logo keeps its centred
   row and the control always sits on the edge it moves. 26px square, centre on
   the border: left = width - 13. Narrow windows keep the page-corner button
   (the strip layout has no side border to sit on). */
@media (min-width: 861px) {
  .navtoggle { top: 26px; left: 231px; width: 26px; height: 26px;
    box-shadow: 0 1px 3px rgb(0 0 0 / .08); }
  .navtoggle svg { width: 14px; height: 14px; }
  .nav-rail .navtoggle { left: 43px; }
  :root:not(.nav-rail) .side { padding-top: 20px; }
}

/* --- Icon rail: the sidebar collapsed on a desktop-width window ----------
   (.nav-rail, set by layout.html). A 56px column of section icons instead of
   no sidebar at all: the page gets its width back without the 56px blank row
   that hiding the sidebar cost (the toggle floated over the page, so every
   page was pushed down to clear it), and every module stays one hover away.
   A group's links open as a flyout beside its icon. Narrow windows keep
   .nav-off, where the strip layout has no room for a rail. */
@media (min-width: 861px) {
  .nav-rail .shell { grid-template-columns: 56px minmax(0, 1fr); }
  /* 8 + 40 + 7 + 1px border = 56: every square is 40×40 on one centre line.
     Top padding clears the edge tab (26–52px) above the first icon. */
  .nav-rail .side { padding: 64px 7px 12px 8px; overflow: visible; }
  /* .side is its own stacking context (sticky + view-transition-name) at z auto,
     so the flyout's z-index 30 only ranks inside it: positioned page content
     (chart labels, sticky table heads) painted over the flyout and read as
     see-through. Lift the whole rail above the page, under .navtoggle (20). */
  .nav-rail .side { z-index: 19; }
  .nav-rail .side .brand { display: none; }
  .nav-rail .nav { overflow: visible; gap: 4px; }

  /* Icon only: one 40px square per section. */
  .nav-rail .nav > a,
  .nav-rail .nav > .grouplabel,
  .nav-rail .navgroup > summary > a,
  .nav-rail .navgroup > summary > .grouplabel {
    position: relative; justify-content: center; gap: 0;
    min-height: 40px; margin: 0; padding: 0;
  }
  .nav-rail .nav > a > span,
  .nav-rail .nav > .grouplabel > span,
  .nav-rail .navgroup > summary > a > span,
  .nav-rail .navgroup > summary > .grouplabel > span,
  .nav-rail .navgroup .chev { display: none; }
  .nav-rail .navgroup > summary { min-height: 40px; }
  /* The section you are in keeps its pill, on the icon. */
  .nav-rail .navgroup:has(.subnav a.on) > summary { background: var(--brand); }
  .nav-rail .navgroup:has(.subnav a.on) > summary > * { color: #fff; }
  /* Hover must not swap in the row tint (.nav a:hover paints the inner link
     near-white) under the white icon — the icon vanished. */
  .nav-rail .navgroup:has(.subnav a.on) > summary:hover { background: var(--brand-dark); }
  .nav-rail .navgroup:has(.subnav a.on) > summary > a:hover { background: transparent; }

  /* Flyout. A closed <details> hides its content through ::details-content;
     the rail shows it on hover / keyboard focus instead of on toggle. The
     summary's ::after bridges the gap to the flyout so the pointer can cross. */
  .nav-rail .navgroup { position: relative; }
  .nav-rail .navgroup::details-content { content-visibility: visible; }
  .nav-rail .navgroup > summary::after {
    content: ""; position: absolute; top: 0; left: 100%; width: 12px; height: 40px;
  }
  .nav-rail .navgroup .subnav {
    display: none; position: absolute; top: 0; left: calc(100% + 9px); z-index: 30;
    min-width: 200px; margin: 0; padding: 4px;
    background: #fff; border: 1px solid var(--line);
    box-shadow: 0 6px 18px rgb(0 0 0 / .08);
  }
  .nav-rail .navgroup:hover .subnav,
  .nav-rail .navgroup:focus-within .subnav { display: flex; }
  .nav-rail .subnav::before {
    content: attr(data-label); padding: 6px 10px 4px;
    font-size: 12px; font-weight: 700; letter-spacing: 0.03em; text-transform: uppercase;
    color: var(--muted); white-space: nowrap;
  }
  .nav-rail .subnav a, .nav-rail .subnav-empty { white-space: nowrap; border-radius: 0; }

  /* A section without children: its name as a tooltip beside the icon. */
  .nav-rail .nav > a:hover::after,
  .nav-rail .nav > a:focus-visible::after,
  .nav-rail .nav > .grouplabel:hover::after {
    content: attr(data-label); position: absolute; left: calc(100% + 9px); z-index: 30;
    padding: 6px 10px; background: var(--ink); color: #fff;
    font-size: 12px; font-weight: 600; letter-spacing: 0; text-transform: none; white-space: nowrap;
  }

  /* Account: avatar over sign-out, the name/role column dropped. */
  .nav-rail .account { flex-direction: column; gap: 6px; margin: auto -8px 0; padding: 12px 0 0; }
  .nav-rail .account-id { display: none; }
  .nav-rail .account form { margin: 0; }
}

/* Dashboard page heading. */
.welcome { font-size: clamp(22px, 3vw, 30px); margin: 0 0 4px; letter-spacing: -0.02em; }
.welcome-sub { margin: 0 0 24px; color: var(--muted); font-size: 15px; }


/* --- Laba Rugi ----------------------------------------------------------- */

/* The report needs the width; the login card does not. .wide widens only here. */
.page.wide { max-width: 1600px; }

.filters {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: 12px;
  margin: 0 0 16px;
}
/* flex column, not the default inline label: a <select>'s and an <input>'s
   inline baselines don't match even at identical box height, which used to
   throw off where each label's own text sat when the row aligned by baseline. */
.filters label { margin: 0; display: flex; flex-direction: column; }
.filters span { display: block; margin-bottom: 6px; }
.filters select, .filters input[type=text], .filters input[type=search], .filters input[type=date], .filters input[type=month], .filters input[type=time] {
  min-height: var(--tap);
  margin: 0; /* overrides the login page's bare `input { margin-bottom: 18px }` */
  padding: 10px 12px;
  font: inherit;
  font-size: 16px;   /* iOS zooms on focus below 16px */
  color: var(--ink);
  background: var(--field);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-sizing: border-box;
}
.filters button { width: auto; height: var(--tap); padding: 0 20px; }

.scope-line { margin: 0 0 16px; font-size: 15px; }
.scope-line span { color: var(--muted); }

/* Four figures, readable from the back of a room (PRD A11). */
.tiles {
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  margin: 0 0 20px;
}
.tile {
  padding: 16px 18px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.tile-label { display: block; margin-bottom: 6px; font-size: 13px; color: var(--muted); }
.tile-value {
  display: block;
  font-size: clamp(20px, 2.2vw, 30px);
  font-weight: 700;
  letter-spacing: -0.02em;
  /* Tabular figures so a column of numbers lines up and a changing period does
     not make the tile jitter. */
  font-variant-numeric: tabular-nums;
}
.tile-value.neg { color: #a3301a; }
/* §6 grades its Haircut / Barber figure vs the 300 target — red miss, green hit,
   the same binary rule §1's per-product scorecard uses. */
.tile-value.pos { color: #1e6b33; }

/* Same card language as .tile/.table-scroll (white, bordered, radius) — .card
   supplies that; this only adds the padding and spacing a chart card needs
   that a login card (.card's other user) does not. */
.chart-card { max-width: none; padding: 16px 18px; margin: 0 0 20px; }

/* The table scrolls inside this box, never the page. A horizontally scrolling
   page is how a phone user loses the description column and the row heading at
   the same time.
   `max-height` is load-bearing, not cosmetic — `overflow-x: auto` computes
   `overflow-y` to `auto` too (CSS can't have one axis scroll and the other
   stay `visible`; the non-`visible` axis wins), so this box IS a scroll
   container in Y whether a height is set or not. Without a bound, its own
   height just grows to fit all ~200 rows and nothing inside it ever scrolls,
   which means `position: sticky` on the caption/header below has nothing to
   stick within — they compute their offset against a scrollport that never
   moves and just scroll away with the page. With the bound, this box gets a
   real internal scrollbar (both axes) and the sticky caption + header row
   freeze at its top exactly like an Excel freeze pane, which is the point. */
.table-scroll {
  overflow-x: auto;
  max-height: 70vh;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

/* `separate`, not `collapse`, and this is load-bearing: under `collapse` the
   borders belong to the table and are painted by it, so a border on a sticky
   cell is left behind the moment the box scrolls. Both sticky things here would
   lose their edge — the pinned .desc column and the .thead row. Under
   `separate` the cell owns its border and carries it. It also keeps every rule
   1px without collapse arithmetic, because the rules below only ever set one
   side of any given boundary. */
table.pnl {
  border-collapse: separate;
  border-spacing: 0;
  /* Fixed layout, and the widths come from the <colgroup> in the template. A
     definite `width` is what lets the fixed algorithm run at all; with `auto`
     the browser silently falls back to content-based sizing, which is the bug
     this replaced. `100%` never squeezes below the column total — the fixed
     algorithm takes the larger of the two — so the table keeps its natural
     2752px and .table-scroll scrolls it. */
  table-layout: fixed;
  width: 100%;
  font-size: 14px;
  font-variant-numeric: tabular-nums;
}

/* One declaration per column kind, and every column of that kind is that width.
   ponytail: 140px holds 15 digits+separators at 14px tabular, which clears the
   largest figure in the data today (1.124.851.016,0). Under fixed layout a
   longer one overflows its cell rather than widening the column — bump c-val
   and c-grand together if the numbers ever outgrow it. */
table.pnl col.c-desc  { width: 300px; }
table.pnl col.c-val   { width: 140px; }
table.pnl col.c-pct   { width: 72px; }
table.pnl col.c-grand { width: 160px; }
/* The "Semua angka dalam Rupiah" note. It lives above .table-scroll, not in
   it — see the comment in laba-rugi.html. Nothing about it is sticky because
   nothing scrolls underneath it. */
.table-note {
  margin: 0 0 6px;
  font-size: 13px;
  line-height: 20px;
  color: var(--muted);
}
/* Columns lead, rows recede: a rule on every column boundary, and horizontal
   rules only where a block of figures ends. Two weights, because one uniform
   rule dissolves the pairing this table is built from — an outlet is a value
   and its %, and those two belong together. The soft rule falls inside a pair,
   the full rule closes the outlet. */
table.pnl th, table.pnl td {
  padding: 6px 12px;
  border-right: 1px solid var(--line-soft);
  white-space: nowrap;
}
table.pnl .pct   { border-right: 1px solid var(--line); }
table.pnl .desc  { border-right: 1px solid var(--line); }
table.pnl .grand { border-right: 0; }  /* .table-scroll draws that edge */
table.pnl th.head { border-right: 0; } /* spans the row — it has no neighbour */

/* The only horizontal rules: under the header, and above each row that closes a
   block. `border-top` rather than `border-bottom` because the useful line is the
   one shutting the list of accounts above a subtotal — the accounting
   convention, and the only direction CSS can reach (no previous-sibling
   selector). Section and block headings need none: their fills separate them. */
table.pnl thead th { border-bottom: 1px solid var(--line); }
tr.k-subtotal > *, tr.k-total > *, tr.k-result > *,
tr.k-section > *, tr.k-block > * { border-top: 1px solid var(--line); }

table.pnl td { text-align: right; }
table.pnl thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--bg);
  font-size: 12px;
  font-weight: 650;
  text-align: right;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

/* Pinned description column. Needs its own background or the scrolling figures
   show through it. */
table.pnl .desc {
  position: sticky;
  left: 0;
  z-index: 1;
  background: #fff;
  text-align: left;
  font-weight: 400;
  /* The one column allowed to wrap. Its width is now the <col>'s and nothing a
     cell does can change that, so a long account name wraps to a second line
     instead of spilling across the figures. Figures stay nowrap. */
  white-space: normal;
}
table.pnl thead .desc { z-index: 3; background: var(--bg); }

table.pnl .grand { background: #f0f0ee; font-weight: 650; }
table.pnl thead .grand { background: var(--bg); }

/* The '%' columns (value / gross revenue) are read-only context, muted. */
table.pnl .pct { color: var(--muted); font-size: 12px; }
table.pnl thead .pct { color: var(--muted); font-size: 11px; }

table.pnl .zero { color: #b6b6b6; }
table.pnl .neg  { color: #a3301a; }

/* Hierarchy comes from `kind`, set by the loader, not from parsing labels.
   The `k-` prefix is not decoration. These names come straight from the row
   kind — ACCOUNT, TOTAL, SECTION — and unprefixed they are exactly the words a
   layout class already wants. `.account` is the sidebar's signed-in user block
   (line 351) and it carries `display: flex`, so every `<tr class="account">`
   stopped being a table row and became a flex container: 121 rows sized their
   own cells and left the column grid, which is what pushed the TOTAL rows'
   figures off the right edge. Keep the prefix on anything derived from data. */
tr.k-section .desc {
  padding-top: 18px;
  font-weight: 700;
  font-size: 13px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--brand);
}
tr.k-block .desc   { padding-top: 12px; font-weight: 650; }
tr.k-account .desc { padding-left: 28px; color: var(--muted); }
tr.k-subtotal      { background: #fafafa; }
tr.k-subtotal .desc, tr.k-subtotal td { font-weight: 600; }
tr.k-total, tr.k-result { background: #ececea; }
tr.k-total .desc, tr.k-total td, tr.k-result .desc, tr.k-result td { font-weight: 700; }
tr.k-result .desc { color: var(--brand); }

/* Full-width section / block headings (a single spanning cell). */
table.pnl th.head {
  text-align: left;
  background: #ececea;
  font-weight: 700;
  letter-spacing: 0.04em;
}

/* The heading LABEL is pinned, not its cell. These rows are one cell spanning
   the table, so nothing of them sits in the first column: scroll right to read
   an outlet and 'PENDAPATAN' and 'Penjualan Barang Dagangan' had gone, leaving
   figures under no heading. Sticky on the cell itself does nothing — a sticky
   box may only shift inside its containing block, and this cell already fills
   the row. The inner span has room, so it travels along the band instead.
   `left` matches the cell's own padding, so nothing moves until it has to. */
table.pnl th.head > span {
  position: sticky;
  left: 12px;
  display: inline-block;
}
tr.k-block th.head > span { left: 20px; }
tr.k-section th.head {
  background: var(--brand);
  color: #fff;
  font-size: 13px;
  text-transform: uppercase;
}
/* Indented, because a block heading is always inside a section: 'OPS.' sits
   under 'BEBAN USAHA'. The lighter fill alone left the two reading as peers. */
tr.k-block th.head { padding-top: 14px; padding-left: 20px; color: var(--ink); }

/* Upload block and its flash result. */
.upload {
  margin-bottom: 24px;
  padding: 14px 16px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.upload form { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.upload .hint { flex: 1 1 100%; font-size: 13px; color: var(--muted); }
.upload input[type=file] { font-size: 14px; }
.flash { padding: 10px 14px; border-radius: var(--radius); margin-bottom: 10px; font-size: 14px; }
.flash.ok  { background: #eef6ec; color: #1e6b33; border: 1px solid #cde5cb; }
.flash.err { background: #fdeeee; color: #a3301a; border: 1px solid #f3cccc; white-space: pre-wrap; }

/* FAT › PNL page chrome (2026-09-27 rework). The page is the report: title and
   the two choices that change it on top, a toolbar, then the table filling the
   screen. Corrections and fee rates open in a side drawer instead of stacking
   under the table. */
/* No width cap: the report is a 13-unit table that scrolls sideways, so every
   pixel the window gives it (more still with the sidebar collapsed) is fewer
   columns off-screen. */
.page.wide.pnl { max-width: none; width: 100%; } /* width: .page's auto margins stop the grid stretching it, so it would size to the table */
.pnl-head {
  display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between;
  gap: 16px 32px; margin: 0 0 20px; padding-bottom: 18px; border-bottom: 1px solid var(--line);
}
.pnl-head h1 { margin: 0 0 6px; }
.pnl-head .scope-line { margin: 0; }
.pnl-head .filters { margin: 0; }
.pnl-status { color: #8a5a00; }
.pnl-source-note { margin: 0 0 14px; font-size: 13px; color: var(--muted); max-width: 90ch; }

.pnl-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px; margin: 28px 0 10px; }
/* Segmented control: two radios dressed as one switch; the native input stays
   on top (transparent) so click, keyboard and screen reader all still work. */
.pnl-view { display: inline-flex; border: 1px solid var(--line); background: var(--field); }
.pnl-view label { position: relative; margin: 0; font-weight: 400; }
.pnl-view label + label { border-left: 1px solid var(--line); }
.pnl-view input {
  position: absolute; inset: 0; width: 100%; height: 100%; min-height: 0;
  margin: 0; padding: 0; opacity: 0; cursor: pointer;
}
.pnl-view span { display: block; padding: 0 14px; line-height: 38px; font-size: 13px; font-weight: 600; color: var(--muted); }
.pnl-view input:checked + span { background: var(--ink); color: #fff; }
.pnl-view input:focus-visible + span { outline: 2px solid var(--brand); outline-offset: 2px; }
select.pnl-jump {
  height: 40px; min-width: 230px; padding: 0 10px; font: inherit; font-size: 13px;
  color: var(--ink); background: var(--field); border: 1px solid var(--line); border-radius: 0;
}
.pnl-toggle { display: inline-flex; align-items: center; gap: 8px; margin: 0; font-size: 13px; font-weight: 400; color: var(--ink); cursor: pointer; }
.pnl-toggle input { width: 16px; height: 16px; min-height: 0; margin: 0; accent-color: var(--brand); }
.pnl-panels { display: flex; gap: 8px; margin-left: auto; }
.pnl-panels .btn { width: auto; min-height: 40px; padding: 0 16px; gap: 8px; font-size: 13px; }
.badge {
  display: inline-block; min-width: 20px; padding: 0 6px; line-height: 18px;
  font-size: 11px; font-weight: 700; text-align: center; background: var(--ink); color: #fff;
}
.badge.is-warn { background: #a3301a; }
.pnl-hint { margin: 0 0 10px; font-size: 13px; color: var(--muted); }
.pnl-hint span + span::before { content: " · "; }

/* The report box fills the screen under the toolbar (vh is not rescaled by the
   page zoom, hence the / --z), so a reader scrolls rows inside it with the
   header row and description column frozen. */
.page.pnl { --pnl-top: 12px; }
.nav-off .page.pnl { --pnl-top: 64px; } /* clear the fixed sidebar toggle */
.pnl-toolbar { scroll-margin-top: var(--pnl-top); }
.table-scroll.pnl-box { max-height: max(360px, calc(100vh / var(--z) - 96px - var(--pnl-top))); }
/* "Nilai saja": the % columns collapse out of the fixed grid; cells stay put so
   every figure keeps its own column. */
table.pnl.no-pct col.c-pct { visibility: collapse; }

/* Phone: a narrower description column leaves room for figures beside it, and
   the toolbar stacks into full-width rows instead of a ragged wrap. */
@media (max-width: 600px) {
  table.pnl col.c-desc { width: 170px; }
  select.pnl-jump { flex: 1 1 100%; min-width: 0; }
  .pnl-panels { flex: 1 1 100%; margin-left: 0; }
  .pnl-panels .btn { flex: 1; }
}

/* Correctable cells show it on hover; a corrected one carries an amber corner
   so the edit is visible without hovering. */
table.pnl td.editable { cursor: pointer; }
table.pnl td.editable:hover, table.pnl td.editable:focus-visible {
  background: color-mix(in srgb, var(--line-soft) 70%, #fff); outline: 1px solid var(--line);
}
table.pnl td.corrected { background: #fff6e0; box-shadow: inset 3px 0 0 #c98a00; }
table.pnl.hide-blank tr.blank { display: none; }
table.pnl td.flash-cell { animation: pnl-flash 1.6s ease-out; }
@keyframes pnl-flash { from { background: #ffd978; } }

/* Side drawer: a native modal <dialog> pinned to the right edge. Painted at true
   scale like dialog#konfirmasi (zoom cancels the page's --z). */
dialog.drawer {
  position: fixed; inset: 0 0 0 auto; margin: 0; padding: 0;
  width: min(520px, 100vw); max-width: 100vw; height: 100%; max-height: none;
  border: 0; border-left: 1px solid var(--line); border-radius: 0;
  background: var(--field); color: var(--ink);
  box-shadow: -24px 0 48px -16px rgba(20, 20, 20, 0.3);
  zoom: calc(1 / var(--z));
}
dialog.drawer[open] { display: flex; flex-direction: column; animation: drawer-in 0.18s ease-out; }
dialog.drawer:focus-visible { outline: none; } /* the close button carries focus, not the whole panel */
dialog.drawer::backdrop { background: rgba(20, 20, 20, 0.4); }
@keyframes drawer-in { from { transform: translateX(32px); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { dialog.drawer[open] { animation: none; } }
.drawer-head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
  padding: 22px 24px 18px; border-bottom: 1px solid var(--line);
}
.drawer-head h2 { margin: 0; font-size: 20px; font-weight: 700; line-height: 1.2; }
.drawer-head p { margin: 4px 0 0; font-size: 13px; color: var(--muted); }
.drawer-close {
  flex: none; width: 40px; height: 40px; min-height: 0; padding: 0;
  background: none; color: var(--muted); border: 1px solid transparent; border-radius: 0;
  font-size: 26px; font-weight: 400; line-height: 1;
}
.drawer-close:hover { color: var(--ink); border-color: var(--line); }
.drawer-body { flex: 1; overflow-y: auto; padding: 20px 24px 40px; scrollbar-width: thin; }
.drawer-body .flash { margin-bottom: 16px; }
.drawer-note { margin: 0 0 16px; font-size: 13px; line-height: 1.5; color: var(--muted); }
.drawer-legend { margin: 8px 0 0; font-size: 12px; color: var(--muted); }
.drawer-empty { margin: 0; padding: 48px 0 8px; text-align: center; color: var(--ink); font-weight: 600; }
.drawer-empty + .drawer-note { text-align: center; }

/* Current fee rates: one row per outlet, a rate is a button that fills the form. */
table.rates { width: 100%; border-collapse: collapse; font-size: 14px; }
table.rates th, table.rates td { height: 46px; padding: 0 12px; border-bottom: 1px solid var(--line-soft); text-align: left; }
table.rates thead th {
  height: 32px; font-size: 11px; font-weight: 650; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--muted); border-bottom: 1px solid var(--line);
}
table.rates tbody th { font-weight: 600; }
table.rates .num { width: 34%; text-align: right; font-weight: 600; }
table.rates.can-edit td { padding: 0; }
button.rate {
  display: flex; flex-direction: column; align-items: flex-end; justify-content: center;
  width: 100%; min-height: 46px; padding: 4px 12px;
  background: none; color: var(--ink); border: 0; border-radius: 0; font: inherit; font-weight: 600;
}
button.rate:hover { background: var(--line-soft); }
button.rate.is-picked { box-shadow: inset 0 0 0 2px var(--brand); }
table.rates .since { display: block; font-size: 11px; font-weight: 400; color: #8a5a00; }

.rate-form {
  display: grid; grid-template-columns: 1fr 1fr; gap: 14px;
  margin: 28px 0 0; padding-top: 22px; border-top: 1px solid var(--line);
}
.rate-form h3 { grid-column: 1 / -1; margin: 0; font-size: 16px; }
.rate-form .drawer-note { grid-column: 1 / -1; margin: -8px 0 0; }
.rate-form label { display: flex; flex-direction: column; gap: 6px; margin: 0; font-size: 12px; font-weight: 600; color: var(--muted); }
.rate-form .full { grid-column: 1 / -1; }
.rate-form input, .rate-form select {
  width: 100%; min-height: 42px; margin: 0; padding: 8px 10px; font: inherit; font-size: 15px;
  color: var(--ink); background: var(--field); border: 1px solid var(--line); border-radius: 0;
}
.rate-form input[name=persen] { font-variant-numeric: tabular-nums; }

.history { margin-top: 28px; border-top: 1px solid var(--line); }
.history summary { padding: 16px 0 8px; font-size: 15px; font-weight: 650; cursor: pointer; }

/* A list of dated entries (corrections, rate changes): what, figures, why/who. */
ul.entries { list-style: none; margin: 0; padding: 0; }
ul.entries li { padding: 16px 0; border-bottom: 1px solid var(--line-soft); }
ul.entries li.is-moved { margin: 0 -24px; padding: 16px 24px; background: color-mix(in srgb, #a3301a 6%, transparent); }
.entry-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; font-size: 15px; }
.entry-head a { font-weight: 650; color: var(--ink); text-decoration: underline; text-decoration-color: var(--line); text-underline-offset: 3px; }
.entry-head a:hover { color: var(--brand); text-decoration-color: currentColor; }
.entry-head > span { font-size: 13px; color: var(--muted); white-space: nowrap; }
.entry-head .entry-rate { font-size: 15px; font-weight: 700; color: var(--ink); }
.entry-figs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin: 12px 0 0; }
.entry-figs dt { font-size: 11px; font-weight: 650; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); }
.entry-figs dd { margin: 2px 0 0; font-size: 14px; font-weight: 600; font-variant-numeric: tabular-nums; }
.entry-figs dd.neg { color: #a3301a; }
.entries .moved { margin: 10px 0 0; font-size: 12px; line-height: 1.5; color: #a3301a; }
.entry-meta { margin: 8px 0 0; font-size: 13px; color: var(--muted); }
.entries form { margin: 6px 0 0; }
/* Quiet until pointed at: a list of ten entries is not ten red alarms. */
button.aksi-batal {
  width: auto; min-height: 0; padding: 0; background: none; border: 0; border-radius: 0;
  color: var(--muted); font: inherit; font-size: 13px; font-weight: 600;
  text-decoration: underline; text-decoration-color: var(--line); text-underline-offset: 3px; cursor: pointer;
}
button.aksi-batal:hover, button.aksi-batal:focus-visible { color: #a3301a; text-decoration-color: currentColor; }

/* Confirm dialog, same register as #koreksi-dialog. Cancel is formmethod=dialog: closes, posts nothing. */
dialog.confirm-dialog {
  width: min(420px, calc(100vw - 32px)); padding: 24px 26px; border: 0; border-radius: 0;
  box-shadow: 0 24px 48px -12px rgba(20, 20, 20, 0.4); zoom: calc(1 / var(--z));
}
dialog.confirm-dialog::backdrop { background: rgba(20, 20, 20, 0.5); }
dialog.confirm-dialog h2 { margin: 0 0 8px; font-size: 20px; line-height: 1.2; }
dialog.confirm-dialog p { margin: 0; color: var(--muted); font-size: 14px; line-height: 1.5; }
dialog.confirm-dialog .k-actions {
  display: flex; flex-direction: row-reverse; justify-content: flex-start; gap: 10px;
  margin: 22px -26px 0; padding: 18px 26px 0; border-top: 1px solid var(--line-soft);
}
dialog.confirm-dialog .k-actions button { width: auto; min-height: var(--tap); padding: 0 22px; border-radius: 0; font-size: 15px; }
dialog.confirm-dialog button.secondary { background: var(--field); color: var(--ink); border: 1px solid var(--line); }
dialog.confirm-dialog button.danger { background: #a3301a; color: #fff; border: 1px solid #a3301a; }

/* The correction dialog, in the same register as dialog#konfirmasi. */
#koreksi-dialog {
  width: min(460px, calc(100vw - 32px)); padding: 24px 26px; border: 0; border-radius: 0;
  box-shadow: 0 24px 48px -12px rgba(20, 20, 20, 0.4); zoom: calc(1 / var(--z));
}
#koreksi-dialog::backdrop { background: rgba(20, 20, 20, 0.5); }
#koreksi-dialog h2 { margin: 0 0 4px; font-size: 20px; line-height: 1.2; }
#koreksi-dialog .k-where { margin: 0 0 14px; color: var(--muted); font-size: 13px; }
#koreksi-dialog .k-now { display: flex; justify-content: space-between; margin: 0; padding: 10px 12px; background: var(--line-soft); font-size: 13px; }
#koreksi-dialog .k-now dt { color: var(--muted); }
#koreksi-dialog .k-now dd { margin: 0; font-weight: 600; font-variant-numeric: tabular-nums; }
#koreksi-dialog .k-note { margin: 6px 0 0; padding: 6px 10px; background: #fff6e0; font-size: 12px; color: #6b4a00; }
#koreksi-dialog label { display: grid; gap: 6px; margin: 14px 0 0; font-size: 12px; font-weight: 600; color: var(--muted); }
#koreksi-dialog input, #koreksi-dialog textarea, #koreksi-dialog select {
  width: 100%; min-height: 42px; margin: 0; padding: 8px 10px; font: inherit; font-size: 15px;
  color: var(--ink); background: var(--field); border: 1px solid var(--line); border-radius: 0;
}
#koreksi-dialog input[name=nilai] { font-variant-numeric: tabular-nums; text-align: right; }
#koreksi-dialog .k-delta { min-height: 1.2em; margin: 6px 0 0; font-size: 12px; font-weight: 600; color: #1e6b33; text-align: right; font-variant-numeric: tabular-nums; }
#koreksi-dialog .k-delta.neg { color: #a3301a; }
#koreksi-dialog .k-actions {
  display: flex; justify-content: flex-end; gap: 10px;
  margin: 22px -26px 0; padding: 18px 26px 0; border-top: 1px solid var(--line-soft);
}
#koreksi-dialog .k-actions button { width: auto; min-height: var(--tap); padding: 0 22px; border-radius: 0; font-size: 15px; }
#koreksi-dialog button.secondary { background: var(--field); color: var(--ink); border: 1px solid var(--line); }

/* Admin hub. The global `input`/`label`/`button`/`.hint` rules are the login page's (full-width
   controls, an 18px bottom margin, a negative-margin hint) and `.page.admin form label span`
   makes every span a block, so everything here is scoped under `.page.admin` to outrank them. */
.page.admin { --ctl-h: 40px; }
.page.admin .page-head { margin-bottom: 4px; }
.page.admin > .hint { margin: 0 0 20px; }

.page.admin .admin-filters { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end; margin: 0 0 12px; }
.page.admin .admin-filters label { margin: 0; }
.page.admin .admin-filters label > span { display: block; margin: 0 0 4px; font-size: 13px; color: var(--muted); }
.page.admin .admin-filters .grow { flex: 1 1 260px; }
.page.admin .admin-filters input, .page.admin .admin-filters select {
  width: 100%; height: var(--ctl-h); min-height: 0; margin: 0; padding: 0 12px; box-sizing: border-box;
  font: inherit; font-size: 14px; font-weight: 400; color: var(--ink);
  background-color: var(--field); border: 1px solid var(--line); border-radius: 8px; }
.page.admin .admin-filters select { width: auto; min-width: 160px; padding-right: 30px; }
.page.admin .admin-filters .btn { width: auto; height: var(--ctl-h); min-height: 0; padding: 0 16px; font-size: 14px; }
.page.admin .count { margin: 0 0 8px; font-size: 13px; color: var(--muted); }

.page.admin .user-list { margin: 0; }
.page.admin .user-list th { text-transform: none; font-size: 13px; letter-spacing: 0; }
.page.admin .user-list td { vertical-align: middle; padding: 10px; }
.page.admin .user-list .row-link { display: block; color: inherit; text-decoration: none; }
.page.admin .user-list .row-link strong { display: block; font-weight: 600; }
.page.admin .user-list .row-link:hover strong { color: var(--brand); }
.page.admin .user-list .row-link small { display: block; color: var(--muted); font-size: 13px; }
.page.admin .user-list tr.is-off td { color: var(--muted); }
.page.admin .user-list .chip { display: inline-block; margin: 0 4px 2px 0; }
.page.admin .status-dot { white-space: nowrap; }
.page.admin .status-dot::before { content: ""; display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  margin-right: 6px; background: var(--muted); }
.page.admin .status-dot.on::before { background: #1e6b33; }

.page.admin .admin-form { display: grid; gap: 16px; max-width: 720px; margin: 0 0 24px; }
.page.admin .admin-card { background: #fff; border: 1px solid var(--line); border-radius: var(--radius); padding: 20px; }
.page.admin .admin-card h2 { font-size: 16px; margin: 0 0 14px; }
.page.admin .admin-card > label { display: block; margin: 0 0 14px; font-weight: 400; }
.page.admin .admin-card > label > span { display: block; margin: 0 0 4px; font-size: 13px; font-weight: 600; color: var(--ink); }
.page.admin .admin-card input[type=text], .page.admin .admin-card input[type=email],
.page.admin .admin-card input[type=password], .page.admin .admin-card select {
  display: block; width: 100%; height: var(--ctl-h); min-height: 0; margin: 0; padding: 0 12px; box-sizing: border-box;
  font: inherit; font-size: 14px; border: 1px solid var(--line); border-radius: 8px;
  background-color: var(--field); color: var(--ink); }
.page.admin .admin-card input:disabled { background: var(--line-soft); color: var(--muted); }
.page.admin .admin-card .hint { display: block; margin: 4px 0 0; font-size: 13px; color: var(--muted); }
.page.admin .admin-card p.hint { margin: 12px 0 0; }
.page.admin .admin-card .btn { width: auto; height: var(--ctl-h); min-height: 0; padding: 0 16px; font-size: 14px; }
.page.admin .admin-card .checkline { display: flex; align-items: center; gap: 8px; margin: 0; font-weight: 600; }
.page.admin .admin-form .page-actions { margin-top: 4px; }
.page.admin .admin-form > .page-actions .btn { width: auto; height: var(--ctl-h); min-height: 0; padding: 0 20px; font-size: 14px; }

.page.admin .role-group { border: 0; padding: 0; margin: 0 0 14px; min-width: 0; }
.page.admin .role-group legend { padding: 0; margin: 0 0 4px; font-size: 12px; font-weight: 600; color: var(--muted);
  text-transform: uppercase; letter-spacing: .04em; }
.page.admin .role-opt { display: grid; grid-template-columns: 18px 1fr; column-gap: 10px; align-items: start;
  margin: 0; padding: 8px 8px; border-radius: 6px; font-weight: 400; cursor: pointer; }
.page.admin .role-opt:hover { background: var(--line-soft); }
.page.admin .role-opt input[type=checkbox] { width: 16px; height: 16px; min-height: 0; margin: 2px 0 0; padding: 0;
  accent-color: var(--brand); }
.page.admin .role-opt-text { display: block; margin: 0; }
.page.admin .role-opt-text strong { display: block; font-size: 14px; font-weight: 600; }
.page.admin .role-opt-text small { display: block; margin: 0; font-size: 13px; line-height: 1.35; color: var(--muted); }
.page.admin .role-outlet { display: block; margin: 8px 0 0; padding-top: 14px; border-top: 1px solid var(--line-soft); }
.page.admin .role-outlet[hidden] { display: none; }
.page.admin .role-outlet > span { display: block; margin: 0 0 4px; font-size: 13px; font-weight: 600; }
.page.admin .admin-card .checkline input[type=checkbox] { width: 16px; height: 16px; min-height: 0; margin: 0;
  accent-color: var(--brand); }

/* Admin — user accounts (PRD §6.2). Same plain-form register as HRD/SQ entry
   pages: an internal utility screen, not something company-facing. */
.users-table { width: 100%; border-collapse: collapse; margin: 0 0 28px; font-size: 14px; }
.users-table th, .users-table td { padding: 8px 10px; border-bottom: 1px solid var(--line); text-align: left; }
.users-table th { color: var(--muted); font-weight: 600; font-size: 12px; text-transform: uppercase; }
/* Column rules. The sheet is otherwise hairline-only — rows carry a horizontal
   line and nothing separates the columns — which is fine at three columns and
   not at nine: the eye has no way to hold a column once the rows are full of
   names and outlets of different lengths. `--line-soft` is the token for exactly
   this (V-scoped hairline, lighter than the row rules so the rows still read as
   the stronger division). Only between columns (`+ th`/`+ td`), so the table's
   left and right edges stay open the way the sheet's other boxes are. */
.users-table th + th,
.users-table td + td { border-left: 1px solid var(--line-soft); }

/* Mutation's ten columns, at fixed widths — and measured, not eyeballed: each is
   the widest thing that column has to hold (its header at 12px/600, or the longest
   value in the real export at 14px — including the two name marks and the row
   switch's own button padding) plus the 20px cell padding, over a 1400px table.
   `table-layout: fixed` takes the widths from the header row, which never changes,
   so filtering the table cannot re-measure the columns as it did under auto layout.

   `min-width` is the sum of those needs. With fixed layout a cell narrower than its
   content neither wraps nor widens the table — it spills over its neighbour — so
   below 1400px the table stops shrinking and the wrapper scrolls instead: a
   scrollbar is honest about not fitting, overlapping text is not. */
.users-table.mutasi { table-layout: fixed; min-width: 1400px; }
/* By column class, like the archive list: Klasifikasi and Status Kerja have
   their own columns now (they were two letter-marks trailing the name), so
   each editable field has a whole cell for its select. */
.mutasi .c-no     { width: 4%; }
.mutasi .c-nib    { width: 7%; }
.mutasi .c-nama   { width: 15%; }
.mutasi .c-posisi { width: 14%; }
.mutasi .c-divisi { width: 11%; }
.mutasi .c-outlet { width: 9%; }
.mutasi .c-klas   { width: 9.5%; }
.mutasi .c-status { width: 8.5%; }
.mutasi .c-masa   { width: 10%; }
.mutasi .c-aktif  { width: 8%; }
.users-table.mutasi th.edit-cell { width: 4%; text-align: center; }
.users-table.mutasi td.edit-cell { text-align: center; overflow: visible; }
.users-table.mutasi td { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mutasi .nama-cell { font-weight: 600; }

/* The leaver view's control. Sits with the filter bar's inputs rather than above
   them: it is the same kind of thing, a view of the list, and it should line up
   with what it changes. align-self bottom-aligns it with the search input,
   both 44px (--tap) tall inside the label's taller (label text + input) column. */
.filters .toggle-nonaktif { align-self: flex-end; }

/* A real button, not just a link that looks clickable — the two controls that
   start something (add a person, change which rows are on screen) read as
   actions instead of blending into the muted text around them. `.btn` is the
   outline/secondary form (same look as the confirm dialog's Batal button);
   `.btn-utama` is the filled/primary form (same look as its Catat button) —
   reused rather than invented, so a button means the same thing everywhere
   in the app. Still an <a>, because these are real navigation, not a script
   action (R-26) — styled to match, not swapped for a <button>. */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--tap); padding: 0 20px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--field); color: var(--ink);
  font: inherit; font-weight: 650; text-decoration: none; cursor: pointer;
}
.btn:hover { border-color: var(--brand); color: var(--brand); }
.btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.btn-utama { background: var(--brand); border-color: var(--brand); color: #fff; }
.btn-utama:hover { background: var(--brand-dark); border-color: var(--brand-dark); color: #fff; }

.page-actions { display: flex; gap: 10px; }

/* A page's own action links, sitting in .hint blocks for the muted intro/fact
   text around them. Plain .hint a would otherwise fall to the browser default
   link color — off the app's one accent, and out of step with .toggle-nonaktif
   elsewhere on these same pages. */
.aksi-link { color: var(--brand); font-weight: 600; }
.aksi-link:hover { color: var(--brand-dark); }
/* Notice Period's cancel is a <button> (a real form submit, not a GET link) but
   reads as the same kind of action text as every other .aksi-link — the base
   sheet's bare `button { width: 100% }` is what it has to override. */
button.aksi-link { background: none; border: 0; padding: 0; width: auto; font: inherit; cursor: pointer; }


/* The filing page's header — name, NIB, source, actions, the footnote about
   where the data comes from — closed off with a hairline so the page reads as
   header, then body, instead of those small grey lines flowing straight into
   IDENTITAS with nothing marking where one ends and the other begins. */
/* The layout's content column shrink-wraps its page; nothing on this one is
   wide enough to hold it open, so it takes the full measure explicitly. */
.page.page-filing { width: 100%; }
.filing-head { margin-bottom: 32px; }
.filing-title { display: flex; justify-content: space-between; align-items: flex-start;
  gap: 16px; flex-wrap: wrap; }
.page .filing-head h1 { font-size: 30px; line-height: 1.15; letter-spacing: -0.015em; margin: 0 0 8px; }
.filing-head .scope-line { display: flex; align-items: center; gap: 10px; margin: 0 0 12px; }
.filing-head .scope-line .angka { color: var(--muted); font-variant-numeric: tabular-nums; }
/* The key facts under the name: one hairline band, label over value, as many
   across as the width takes. */
.ringkas { margin: 16px 0 0; padding: 14px 0; display: grid; gap: 14px 24px;
  grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
  border-top: 1px solid var(--ink); border-bottom: 1px solid var(--line); }
.ringkas dt { margin: 0 0 4px; font-size: 11px; font-weight: 700; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.08em; }
.ringkas dd { margin: 0; font-size: 16px; color: var(--ink); }
.angka { font-variant-numeric: tabular-nums; }
/* A field the filing does not have yet: stated, and set apart from a value so a
   scan down the page finds the gaps. */
.kosong { display: inline-block; padding: 0 6px; border: 1px dashed #b5b5b5;
  color: var(--muted); font-size: 12px; line-height: 18px; }
.rahasia { display: inline-flex; align-items: baseline; gap: 12px; white-space: nowrap; }
/* Two columns of label/value pairs at the filing's measure, never three: a
   third squeezed the 16-digit numbers onto two lines. */
.page-filing .filing dl { grid-template-columns: repeat(auto-fit, minmax(max(320px, 40%), 1fr)); }
.rahasia .aksi-link { font-size: 13px; min-height: 0; height: auto; }
.filing-foot { margin-top: 8px; padding-top: 14px; border-top: 1px solid var(--line); }
.filing-foot .hint { margin: 0 0 4px; font-size: 12px; }

/* The roster marks trailing a name. Shape carries the attribute — circle is
   klasifikasi, square is status kerja — and the letter is the value's initial.
   The shape is what says which of the two a mark is: both columns draw the same
   kind of badge, and the two "P"s that started this (PROBATION a grade,
   PERBANTUAN a status) are the case that made the distinction necessary — though
   PERBANTUAN is no longer one of the categories below, so it draws nothing now.

   A mark is drawn only for a value HR counts as one of the two categories:
   klasifikasi is PROBATION / CLASSIC / GRAND, status kerja is TETAP / KONTRAK,
   and those five lines below are the whole vocabulary. Anything else draws
   NOTHING — no neutral placeholder — because these columns are reused for other
   things in the export: PUSAT's KLASIFIKASI is that sheet's active flag (every
   row reads "AKTIF"), and the RESIGN sheet puts employment states in both columns
   ("NON AKTIF", "TIDAK AKTIF", "TRAINING", and one cell holding the header text
   "sTATUS KERJA"). Drawing them in grey made nonsense look like a category. A new
   real grade needs a line here, and that is the one place to add it.

   Matched case-insensitively (`i`): the RESIGN sheet writes "Tetap" and
   "Probation" where the others write "TETAP" and "PROBATION", and a case variant
   is the same category, not a different one. */
.nama-mark { display: flex; align-items: center; gap: 5px; }
.mark { flex: none; width: 17px; height: 17px; display: none;
  align-items: center; justify-content: center;
  color: #fff; font-size: 10px; font-weight: 700; line-height: 1; }
.mark-circle { border-radius: 50%; }
.mark-square { width: 15px; height: 15px; }
/* Klasifikasi — the grade ladder. */
.mark[data-value="PROBATION" i] { display: inline-flex; background: #1d4ed8; }
.mark[data-value="CLASSIC" i]   { display: inline-flex; background: #0f766e; }
.mark[data-value="GRAND" i]     { display: inline-flex; background: #6d28d9; }
/* Status kerja — the contract type. */
.mark[data-value="TETAP" i]     { display: inline-flex; background: #15803d; }
.mark[data-value="KONTRAK" i]   { display: inline-flex; background: #c2410c; }

/* --- Employee Archive: the filing page ------------------------------------ */
/* ~40 fields, so not a table: a label/value grid whose pairs reflow into two
   columns on a wide screen. Each pair carries its own hairline, which is what
   keeps a long run of short facts scannable instead of a wall of text. */
.filing { margin: 0 0 30px; }
.filing h2 { margin: 0 0 12px; font-size: 12px; font-weight: 700; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.08em; }
.filing dl { margin: 0; display: grid; gap: 4px 32px;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.filing dl > div { display: grid; grid-template-columns: 11rem minmax(0, 1fr);
  gap: 12px; padding: 5px 0; border-bottom: 1px solid var(--line-soft); }
.filing dt { color: var(--muted); font-size: 13px; }
.filing dd { margin: 0; font-size: 14px; overflow-wrap: anywhere; }

/* "live" marks a value read from the current roster rather than from the file.
   The footer says so in words; this is the per-field pointer to which ones. */
.live { margin-left: 6px; padding: 1px 5px; background: var(--brand); color: #fff;
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; }

/* Which sheet a filing came from — Resign being the one worth spotting. */
.sheet-badge { display: inline-block; padding: 2px 8px; border: 1px solid var(--line);
  color: var(--muted); font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.06em; }
.sheet-badge.sheet-resign { border-color: var(--brand); color: var(--brand); }

/* The archive's two tabs, as badges stuck to the top edge of the page. Packed to
   the right, so the last one — Resign — sits over the table's last column
   ("Lihat berkas") and the pair reads as belonging to the list below them rather
   than floating in the middle of it. Movable: it is one `justify-content`, and
   `center` is where they were before.

   The negative top margin cancels `.page`'s own padding so the badges sit ON the
   page's top edge instead of floating below it — that is the whole of the
   "sticking from the top" effect, and it is coupled to that padding: change one
   and this has to follow.

   No rule under the strip. There was one, and the badges were pulled down over it
   so the open one's fill ran into the content below; without it the pair simply
   sits on the edge as two square badges sharing a seam, which is the plainer read
   and the one asked for. Square, because nothing on this sheet is round. */
.arsip-tabs { display: flex; justify-content: flex-end; margin: -24px 0 22px; }
.arsip-tabs a {
  padding: 9px 26px;
  border: 1px solid var(--line);
  background: var(--bg); color: var(--muted);
  font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em;
  text-decoration: none;
}
/* Adjacent, not spaced: two badges sharing one edge read as one control. */
.arsip-tabs a + a { border-left: 0; }
.arsip-tabs a.active { background: var(--brand); border-color: var(--brand); color: #fff; }
.arsip-tabs a:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
/* The scroll wrapper both HRD list tables sit in: with fixed widths and a
   min-width, a window narrower than the table needs scrolls this box rather than
   the page — and a page that scrolls sideways takes the nav and the filter bar
   with it, so the reader loses both. The tables' own bottom margin moves here
   because a margin inside a scroll container is scrollable area: left where it
   was, a horizontal scrollbar would sit detached, 28px below the last row. */
.tabel-scroll { overflow-x: auto; margin: 0 0 28px; }
.users-table.arsip-table,
.users-table.mutasi,
.users-table.rehire-table { width: 100%; margin-bottom: 0; }
.users-table.arsip-table th,
.users-table.arsip-table td,
.users-table.rehire-table th,
.users-table.rehire-table td { white-space: nowrap; }
/* Widths by column class, not nth-child: the two tabs carry different columns
   (Klasifikasi/Status/Masa Kerja on Karyawan aktif, Resign/Rehire on Resign).
   Min-width is where a 1400px nine-column table used to be; below it the wrapper
   scrolls. A value longer than its column ends in an ellipsis rather than
   spilling over the next one — "Purchasing Dan Distribusi Staff" is the one. */
.users-table.arsip-table { table-layout: fixed; min-width: 1250px; }
.users-table.arsip-table td { overflow: hidden; text-overflow: ellipsis; }
.arsip-table .c-no     { width: 4%; }
.arsip-table .c-nib    { width: 8%; }
.arsip-table .c-nama   { width: 16%; }
.arsip-table .c-posisi { width: 14%; }
.arsip-table .c-divisi { width: 10%; }
.arsip-table .c-outlet { width: 9%; }
.arsip-table .c-klas   { width: 10%; }
.arsip-table .c-status { width: 9%; }
.arsip-table .c-tgl    { width: 10%; }
.arsip-table .c-masa   { width: 10%; }
.arsip-table.is-resign .c-nama   { width: 16%; }
.arsip-table.is-resign .c-posisi { width: 16%; }
.arsip-table.is-resign .c-outlet { width: 10%; }
.arsip-table.is-resign .c-tgl    { width: 11%; }
.arsip-table.is-resign .c-aksi   { width: 14%; }
.arsip-table .aksi-cell { text-align: right; }
/* NIB and dates line up digit under digit. */
.arsip-table .angka, .mutasi .angka { font-variant-numeric: tabular-nums; }
/* Row number: a counter, not printed text, so a filter (display:none) and a sort
   (DOM reorder) renumber it for free. */
.arsip-table tbody, .mutasi tbody { counter-reset: baris; }
.arsip-table td.c-no, .mutasi td.c-no { font-variant-numeric: tabular-nums; color: var(--muted); }
.arsip-table td.c-no::before, .mutasi td.c-no::before { counter-increment: baris; content: counter(baris); }


/* Sort buttons: the header's own look, with the direction drawn after the label
   once a column is sorted — nothing drawn on the others, so the one sorted column
   is the one mark in the row. */
.arsip-table th, .mutasi th:not(.edit-cell) { padding: 0; }
/* Header filters (col-filter.html): the funnel sits over the th's right edge, the
   panel is the .cf-pop shared with Data Complain. .tabel-scroll keeps a floor
   height so a 0-row result cannot clip an open panel. */
.arsip-table th, .mutasi th { position: relative; }
.arsip-table th.cf-open, .mutasi th.cf-open { z-index: 5; }
.arsip-table th > .cf-btn, .mutasi th > .cf-btn {
  position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  width: 26px; height: 26px; justify-content: center;
}
/* Only Masa Kerja still sorts; every other header is a plain label. */
.arsip-table .th-label, .mutasi .th-label { display: flex; align-items: center; min-height: 40px; padding: 8px 10px;
  box-sizing: border-box; letter-spacing: 0.04em; }
.arsip-table th:has(> .cf-btn) > .th-label, .mutasi th:has(> .cf-btn) > .th-label { padding-right: 32px; }
.tabel-scroll:has(.arsip-table), .tabel-scroll:has(.mutasi) { min-height: 320px; }
.users-table.arsip-table th.c-no, .users-table.mutasi th.c-no { padding: 8px 10px; }
.arsip-table th > button,
.mutasi th > button {
  display: flex; align-items: center; gap: 6px; width: 100%; min-height: 40px;
  padding: 8px 10px; margin: 0; border: 0; background: none; border-radius: 0;
  font: inherit; color: inherit; text-transform: inherit; letter-spacing: 0.04em;
  text-align: left; cursor: pointer;
}
.arsip-table th > button:hover, .mutasi th > button:hover { color: var(--ink); }
.arsip-table th > button:focus-visible, .mutasi th > button:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.arsip-table th[aria-sort] > button, .mutasi th[aria-sort] > button { color: var(--ink); }
.arsip-table th[aria-sort] > button::after, .mutasi th[aria-sort] > button::after {
  content: ""; width: 0; height: 0;
  border-left: 4px solid transparent; border-right: 4px solid transparent;
}
.arsip-table th[aria-sort="ascending"] > button::after, .mutasi th[aria-sort="ascending"] > button::after { border-bottom: 5px solid var(--brand); }
.arsip-table th[aria-sort="descending"] > button::after, .mutasi th[aria-sort="descending"] > button::after { border-top: 5px solid var(--brand); }

/* The whole row opens the filing. The name is the one real link; its ::after
   covers the row, so a click anywhere lands on it while keyboard and screen
   reader still meet a single link per person. Rehire sits above that layer. */
.arsip-table tbody tr { position: relative; }
.arsip-table tbody tr:hover { background: #f6f6f6; }
.arsip-table .baris-link { color: var(--ink); font-weight: 600; text-decoration: none; }
.arsip-table .baris-link::after { content: ""; position: absolute; inset: 0; }
.arsip-table tbody tr:hover .baris-link { color: var(--brand); text-decoration: underline;
  text-underline-offset: 3px; }
.arsip-table .baris-link:focus-visible { outline: none; }
.arsip-table tbody tr:has(.baris-link:focus-visible) { outline: 2px solid var(--brand); outline-offset: -2px; }
.arsip-table .aksi-cell .aksi-link { position: relative; z-index: 1; }

/* Klasifikasi / Status: the word, with the roster page's mark beside it as its
   colour key. .mark hides itself for anything outside the vocabulary. */
.kategori { display: inline-flex; align-items: center; gap: 8px; }
.kosong-sel { color: var(--muted); }


.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
/* Rehire's own list — the same widths above assume the archive list's own
   9 columns (No first, an IN column before OUT); this table has neither
   ("Cari Nama" already narrowed it, so no row number is needed, and a rehire
   candidate is read here by when they left, not when they joined), 7 columns
   in all. Reusing .arsip-table's nth-child rules directly shifted every
   column by one or two and squeezed NIB down to 3.1% ("No"'s width) — this is
   its own set, sized the same measured way, not a shift of that one. */
.users-table.rehire-table { table-layout: fixed; min-width: 1250px; }
.users-table.rehire-table th:nth-child(1) { width: 8%; }    /* NIB */
.users-table.rehire-table th:nth-child(2) { width: 26%; }   /* Nama */
.users-table.rehire-table th:nth-child(3) { width: 15%; }   /* Divisi */
.users-table.rehire-table th:nth-child(4) { width: 19.8%; } /* Posisi */
.users-table.rehire-table th:nth-child(5) { width: 8.1%; }  /* Outlet */
.users-table.rehire-table th:nth-child(6) { width: 12.2%; } /* OUT */
.users-table.rehire-table th:nth-child(7),
.users-table.rehire-table td:nth-child(7) { width: 10.9%; text-align: center; } /* Pilih */
/* Employee Mutation's row switch - same colour pair as .flash.ok/.flash.err,
   since Aktif/Tidak Aktif is the same kind of good/bad signal. Square, like
   every other control on this sheet (--radius: 0, docs/design-swiss.md) - the
   pill shape was a leftover from before that pass, not a decision. */
/* Aktif, at rest: a word and a dot, not a button — nothing on a row nobody
   armed can be pressed, so nothing should look pressable. */
.keaktifan { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; }
.keaktifan::before { content: ""; width: 7px; height: 7px; border-radius: 50%; }
.keaktifan.ya::before { background: #1e8a3e; }
.keaktifan.tidak { color: #a3301a; }
.keaktifan.tidak::before { background: #a3301a; }
/* Armed: the one action the cell offers, outlined in the colour of what it
   records — keluar in the alert red, masuk in the notice green. */
.switch { width: auto; min-height: 30px; padding: 0 10px; border: 1px solid currentColor;
  border-radius: var(--radius); background: var(--field); font: inherit; font-size: 12px;
  font-weight: 700; cursor: pointer; }
.switch.keluar { color: #a3301a; }
.switch.keluar:hover { background: #fdeeee; }
.switch.masuk { color: #1e6b33; }
.switch.masuk:hover { background: #eef6ec; }
.switch:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
/* Employee Mutation row guard, layer 1: the pencil arms one row, and until it
   is clicked the switch's cell sits in a disabled fieldset - fieldset[disabled]
   rather than a disabled button, because it also suppresses Enter-to-submit,
   which a disabled button alone would not. Every other editable cell has no
   button to disable at all (see .cell-edit below): they're simply not in the
   DOM's visible flow until .is-armed says so. UI gate only: the POST handlers
   keep their HRD/ADMIN check and validate the submitted vocabulary, and
   nothing more. */
.row-actions { border: 0; margin: 0; padding: 0; min-inline-size: 0; }

/* The pencil has its own column, last in the table, so that cell's right edge IS
   the table's edge and the icon needs no width borrowed from anywhere. `width:
   1%` is the shrink-to-content idiom — it takes its minimum and leaves the rest
   of the surplus to the columns holding text. An earlier version instead widened
   the Aksi cell to 100%, which swallowed the table's slack and squeezed every
   other column until NIB wrapped to two lines: the narrow explicit column is what
   replaced that. Right-aligning the header too, so it sits over its icon. */
.users-table th.edit-cell,
.users-table td.edit-cell { width: 1%; text-align: right; white-space: nowrap; }
.users-table td.aksi-cell { text-align: right; white-space: nowrap; }
/* Editable cells (Divisi, Outlet, the two fields in Nama): the pencil swaps the
   plain text for the real select in place, rather than opening a popup pointing
   at them — .is-armed on the <tr> is set by the pencil's own click handler. Each
   select still `form`s across to the row's one real <form>, which rides along
   invisibly in the Edit cell (see the template comment there) — there is no
   longer an Aksi cell for it to live in visibly. */
.cell-edit { display: none; }
tr.is-armed .cell-static { display: none; }
tr.is-armed .cell-edit { display: inline-flex; align-items: center; gap: 6px; }
/* The bare selects (Divisi, Outlet) are .cell-edit themselves, not a wrapper
   around one — flex has nothing to lay out inside a <select>, so they get the
   plain inline-block every other control on the sheet uses. */
tr.is-armed select.cell-edit { display: inline-block; }
.cell-edit select,
select.cell-edit { min-height: 32px; padding: 2px 8px; font: inherit; font-size: 13px;
  border: 1px solid var(--ink); border-radius: 0; background-color: var(--field); }
/* select.cell-edit only, not the two nested inside Nama's flex wrapper: this
   table's columns are fixed percentages (table-layout: fixed) sized for text
   — a select's native intrinsic width (padding + dropdown arrow) is wider
   than that and, unlike text, does not wrap. Divisi and Outlet are each the
   whole cell, so 100% + border-box caps them at the column; the Nama pair sit
   in a wide flexible column and size to their own content instead. */
select.cell-edit { width: 100%; max-width: 100%; box-sizing: border-box; }
.row-edit { width: 28px; height: 28px; min-height: 0;
  padding: 0; display: inline-flex;
  align-items: center; justify-content: center; background: none;
  border: 1px solid var(--line); color: var(--muted); }
.row-edit:hover { border-color: var(--brand); color: var(--brand); }
.row-edit.on { background: var(--brand); border-color: var(--brand); color: #fff; }
.row-edit .ikon-tutup, .row-edit.on .ikon-pensil { display: none; }
.row-edit.on .ikon-tutup { display: block; }
.row-edit:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
/* The armed row: tinted from the brand and ruled top and bottom, so which row a
   change will land on is never a question. */
.mutasi tbody tr.is-armed td { background: #f8f1ee; border-bottom-color: var(--brand); }
.mutasi tbody tr:has(+ tr.is-armed) td { border-bottom-color: var(--brand); }
.mutasi tbody tr:not(.is-armed):hover td { background: #f6f6f6; }
.mutasi tr.is-armed td { overflow: visible; }
/* A leaver, listed only with "Tampilkan yang tidak aktif": muted, still legible. */
.mutasi tr.nonaktif td { color: var(--muted); }
.mutasi tr.nonaktif .nama-cell { font-weight: 400; }
.hasil-baris { display: flex; align-items: baseline; gap: 16px; margin: 0 0 12px; }
.hasil-baris .hint { margin: 0; }
.hasil-baris .aksi-link { font-size: 13px; }
/* The stroke trio is the house icon convention (see `.nav svg`): without it the
   paths render as their default black fill and ignore `color`, which made the
   armed pencil a black glyph on the brand brown instead of white. */
.row-edit svg { width: 14px; height: 14px; fill: none; stroke: currentColor;
  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }

/* Employee Archive's view/edit flip: a pencil on the filing page, an eye on the
   edit page it leads to — same icon convention as .row-edit's stroke trio, sized
   for sitting inline with text rather than alone in a table cell. */
.pencil-toggle, .eye-toggle { display: inline-flex; align-items: center; gap: 5px; }
.pencil-toggle svg, .eye-toggle svg { width: 14px; height: 14px; fill: none;
  stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }


/* The person form: the archive's ~39 fields, grouped, at three to a row. Zero radius
   and hairlines like everything else; each group collapsed except the two HR fills in
   every time, because a wall of thirty-nine inputs is a page nobody reads. A collapsed
   <details> still submits what is inside it — hiding a group hides fields, it does not
   drop them. */
.form-karyawan { margin: 0 0 28px; }
.form-karyawan > label { display: flex; flex-direction: column; max-width: 22rem; }
.form-karyawan > label > span,
.form-karyawan .isian label > span { margin-bottom: 4px; font-size: 12px; color: var(--muted); }
.form-karyawan select,
.form-karyawan input { box-sizing: border-box; width: 100%; min-height: 34px; padding: 4px 8px; font: inherit; }
.form-karyawan select { background: #fff; }
.form-karyawan select:disabled,
.form-karyawan input:disabled { background: var(--bg); color: var(--muted); }
/* The archive is kept in capitals: typed text shows as stored (EmployeeArchiveQuery#submitted
   does the actual uppercasing). Placeholders stay as written. */
.form-karyawan input[type=text] { text-transform: uppercase; }
.form-karyawan input[type=text]::placeholder { text-transform: none; }
.form-karyawan input:user-invalid { border-color: #7a2d18; outline: 1px solid #7a2d18; }
.form-karyawan .grup { margin: 14px 0; border: 1px solid var(--line); background: #fff; }
.form-karyawan .grup > details > summary { padding: 10px 14px; cursor: pointer; font-size: 12px;
  font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); }
.form-karyawan .isian { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px; padding: 4px 14px 14px; }
.form-karyawan .isian label { display: flex; flex-direction: column; }
/* The five the roster owns: present, but visibly not this form's to change. */
.form-karyawan .isian label.milik-roster > span::after { content: " ·"; color: var(--brand); }
.form-karyawan > button { margin-top: 4px; }

/* The confirmation banner's delta list — one moved attribute per line. */
.notice .deltas { display: block; font-size: 13px; }

/* Confirmation dialog — the app's own, not the browser's `confirm()` chrome.
   Same native <dialog> primitive as the section modal (top layer, inert
   background, Esc + focus trap for free, no library), and the same
   `zoom: calc(1 / var(--z))` cancel so it paints at true scale like that one
   does rather than at the page's 0.8. Swiss register: zero radius, a hairline
   rule above the actions, uppercase caption, brand as the only accent. */
dialog#konfirmasi, dialog#tambah-dialog {
  padding: 0; border: 0; border-radius: var(--radius);
  background: var(--field); color: var(--ink);
  width: min(440px, calc(100vw - 48px));
  box-shadow: 0 24px 48px -12px rgba(36, 26, 21, 0.4);
  zoom: calc(1 / var(--z));
}
dialog#konfirmasi::backdrop, dialog#tambah-dialog::backdrop { background: rgba(20, 20, 20, 0.55); }
dialog#konfirmasi form, dialog#tambah-dialog form { padding: 26px 26px 0; }
.konfirmasi-caption { margin: 0 0 10px; font-size: 12px; font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }
dialog#konfirmasi h2, dialog#tambah-dialog h2 { margin: 0 0 12px; font-size: 21px; font-weight: 700; line-height: 1.2; }
.konfirmasi-teks { margin: 0; font-size: 15px; line-height: 1.5; }
/* The switch's date-of-effect, and the roster form's note: both asked here,
   inside the confirm they belong to, not on the row ahead of a click nobody
   has made yet — shown only for the form that needs them (hrdMutationConfirm
   sets [hidden] on whichever one doesn't). The `[hidden]` rule below is load-
   bearing, not decoration: `display: flex` on the bare class is an author
   rule, and an author rule beats the UA's own `[hidden] { display: none }`
   regardless of specificity — without it, the hidden field showed anyway
   (the switch's dialog was asking for a note it has nowhere to put). */
.konfirmasi-tanggal { display: flex; flex-direction: column; margin: 16px 0 0; }
.konfirmasi-tanggal[hidden] { display: none; }
.konfirmasi-tanggal > span { margin-bottom: 4px; font-size: 12px; color: var(--muted); }
.konfirmasi-tanggal input { min-height: var(--tap); padding: 4px 8px; font: inherit;
  border: 1px solid var(--line); border-radius: var(--radius); }
.konfirmasi-aksi { display: flex; justify-content: flex-end; gap: 10px;
  margin: 24px -26px 0; padding: 18px 26px; border-top: 1px solid var(--line-soft); }
/* width:auto because the sheet's bare `button` rule is width:100% for the
   plain form pages; these are a right-aligned pair, not a stack. */
.konfirmasi-aksi button { width: auto; min-height: var(--tap); padding: 0 22px;
  font: inherit; font-weight: 650; cursor: pointer; border-radius: var(--radius);
  border: 1px solid var(--line); background: var(--field); color: var(--ink); }
.konfirmasi-aksi button:hover { border-color: var(--brand); color: var(--brand); }
.konfirmasi-aksi button.utama { background: var(--brand); border-color: var(--brand); color: #fff; }
.konfirmasi-aksi button.utama:hover { background: var(--brand-dark); border-color: var(--brand-dark); color: #fff; }
.konfirmasi-aksi button:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.users-table details > summary { cursor: pointer; color: var(--muted); font-size: 13px; }
.users-table details form { margin-top: 6px; display: flex; gap: 6px; }
.users-table details input[type=text] { min-height: 32px; padding: 4px 8px; font: inherit; }
/* Scoped to .admin, not bare .page (2026-09-11 fix): .page is also the
   Dashboard Company shell's class, and this rule set — written for the plain
   admin/users forms — was matching every <select> in that page's own header
   controls too (`.page form select` beat `.period-picker select` on
   specificity), forcing them all to 44px tall, square-cornered, grey admin
   inputs. That is what actually broke the header's proportions, not the
   header's own layout. */
.page.admin form { margin: 0 0 20px; }
.page.admin form label { display: block; margin: 0 0 10px; }
.page.admin form label span { display: block; margin-bottom: 4px; font-size: 13px; color: var(--muted); }
.page.admin form input[type=text], .page.admin form input[type=password], .page.admin form select {
  min-height: var(--tap);
  padding: 8px 10px;
  font: inherit;
  font-size: 16px;
  border: 1px solid var(--line);
  background: var(--field);
}
.page.admin fieldset { border: 1px solid var(--line); margin: 0 0 10px; padding: 10px; }
.page.admin .checkline { display: flex; align-items: center; gap: 8px; margin: 0 0 6px; }
.page.admin .checkline input { width: auto; min-height: auto; }

.download {
  margin-left: auto;
  font-size: 14px;
  font-weight: 600;
  color: var(--brand);
  text-decoration: none;
}
.download:hover { text-decoration: underline; }

.source { margin: 14px 0 0; font-size: 13px; color: var(--muted); }

/* ===================================================================   Dashboard Company
   ===================================================================   The chart palette, decided once here and binding on every later module
   (ADR-0002 §5a left palette, mark specs and tile design to the first
   dashboard). Validated with the dataviz six-checks against the card surface
   (#fff), adjacent pairlist — every chart on the page is a bar, line or stack:

     [PASS] lightness band, chroma floor, contrast >= 3:1
     [PASS] CVD separation, worst adjacent dE 11.2 (deutan), target >= 8
     [PASS] normal-vision floor, worst adjacent dE 18.7, floor >= 15

   The ORDER is the safety mechanism. Rust and gold measure dE 11.4 to each
   other and FAIL the normal-vision floor when adjacent, so teal sits between
   them permanently. Re-run the validator before reordering; three remains the
   cap for any chart using s1-s3 alone, and two on all-pairs forms (scatter,
   bubble, choropleth) if one is ever added. §1's category stack extends this
   to six slots (s4-s6 below) as its own separately-validated set — that
   extension does not raise the cap here for s1-s3's other uses.

   These duplicate the SERIES constant in src/main/frontend/charts.js, which is
   the one place the charts themselves read. Kept in both because the swatches
   in a legend are CSS and the marks are JS; change them together. */
:root {
  --s1: #A8502A;   /* rust — slot 1 */
  --s2: #008C80;   /* teal — slot 2 */
  --s3: #AD7A00;   /* gold — slot 3 */

  /* §1's category stack only (Haircut/Package/Treehouse/F&B/Produk/Layanan
     Lainnya) — six slots, not a bump of the three-slot cap above. Reuses s1-s3
     for the first three buckets (same brand hues) and adds three more,
     validated together as one adjacent-pair set:

       node scripts/validate_palette.js \
         "#A8502A,#008C80,#AD7A00,#3A6EA5,#B5487A,#5C7A29" \
         --mode light --surface "#ffffff"
       → ALL CHECKS PASS. One WARN: adjacent pair s5<->s4 (magenta/blue) sits
         in the 6-8 CVD floor band (deutan 7.2) — legal only with mandatory
         secondary encoding, which this chart already has (direct-labelled
         legend, 2px surface gap between stacked segments). Every other
         adjacent pair and the normal-vision floor (>=15) clear clean.

     Six is the cap for THIS chart specifically (one fixed stack order,
     adjacent-pair form) - re-run the validator before reordering, adding a
     7th, or reusing s4-s6 anywhere pairs could appear in a different order
     (a legend re-sort, a second stacked chart with a different category
     order) - the adjacent pairlist depends on order. */
  --s4: #3A6EA5;   /* blue — slot 4, F&B */
  --s5: #B5487A;   /* magenta — slot 5, Produk */
  --s6: #5C7A29;   /* green — slot 6, Layanan Lainnya */
}

/* `.page` sets `margin: 0 auto`, and an auto inline margin on a grid item
   disables stretch — the item becomes shrink-to-fit. On a table page that is
   harmless (the table drives the width); here it made `main` 919px inside a
   1756px column, so the card grid only ever saw room for two columns and the
   right third of a projector stayed empty. An explicit width restores the fill
   without touching `.page` itself, which Laba Rugi depends on. */
.dash { width: 100%; }

.dash .flash.err { margin-bottom: 20px; }

  .dash-head {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 20px;
  align-items: flex-start;
  justify-content: space-between;
  margin-bottom: 20px;
}
.dash-head h1 { margin-bottom: 4px; font-size: clamp(24px, 3.4vw, 40px); }
.dash-head .scope-line { margin: 0; }
.dash-head-titles { min-width: 0; flex: 1 1 240px; }

/* Three-column band: delta chips left, title centred, mode/period controls
   right, all top-aligned so the header stays one row tall. A hairline rule
   under the band separates the masthead from the card grid (scrolling layout
   only — see the one-screen override). */
.dash-head {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: start;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--line);
}
.dash-head-titles { grid-area: 1 / 2; text-align: center; align-self: center; }
.dash-head .deltas { grid-area: 1 / 1; justify-self: start; }
.dash-head .controls { grid-area: 1 / 3; justify-self: end; margin-left: 0; }
/* When the sidebar is hidden the fixed .navtoggle sits over the page's top-left
   corner — push the delta chips clear of it. */
.nav-off .dash-head .deltas { margin-left: 46px; }

.deltas { display: flex; flex-wrap: wrap; gap: 8px; }
.dash-head .deltas .scope-line { flex-basis: 100%; margin: 2px 0 0; font-size: 13px; }
.delta {
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding: 6px 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 13px;
  color: var(--muted);
  background: #fff;
}
.delta strong { font-size: 14px; font-variant-numeric: tabular-nums; }
/* Target-mode-by-default (2026-09-06): whichever chip leads (Target, or the
   picked comparison once ?bandingkan= is set) gets a filled, bolder treatment;
   the other becomes a plain-bordered secondary. Same .delta shape either way -
   no new pill component, just which one reads as "the headline". */
.delta.delta-primary { background: #f5efe8; border-color: var(--brand); font-weight: 600; }
.delta.delta-primary strong { font-size: 15px; }
.delta.delta-secondary { font-size: 12px; padding: 5px 10px; }
.delta.delta-secondary strong { font-size: 13px; }
/* The arrow, not the colour, is what says "up" — a delta cue beside a series
   colour must never rest on hue alone. */
.delta.up strong { color: #1e6b33; }
.delta.up::after { content: "\25B2"; font-size: 9px; color: #1e6b33; }
/* No red pair for "down" - the page's own cross-cutting rule (see
   dashboard-chart-choices.md) is that green/red is never available, so a
   decline stays the base muted colour with a down arrow, not a red one. */
.delta.down::after { content: "\25BC"; font-size: 9px; }

/* The controls wrapper: the mode toggle and the period picker, side-by-side
   as a single right-aligned group. Each control has a small uppercase caption
   above it naming what it is (Tampilan / Periode), so a sighted user has the
   same context the aria-label gives a screen reader. */
.controls {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
  margin-left: auto;
}
.controls-row {
  display: flex;
  align-items: center;
  gap: 18px;
}
/* Mode toggle, Periode and Bandingkan all sit in one flex row now (2026-09-11).
   A plain `<form>` lays its children out as inline content, which — once
   squeezed by everything else sharing the row — wrapped the weekly Minggu
   select onto a second line inside the Periode form rather than the row
   giving up width. flex + flex-shrink:0 stops that: each group keeps its own
   natural width and never breaks internally. No flex-wrap on the row itself —
   this is a meeting-room dashboard meant for a wide screen (PRD "readable
   from the back of a room"), not a narrow window, and wrapping whole groups
   pushed Bandingkan down onto the card grid below at anything less than
   full width, which reads worse than the row staying one line and, on a
   truly narrow viewport, simply running to the edge. */
.period-picker, .banding-picker {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}
.mode-toggle, .controls-label { flex-shrink: 0; }
.controls-label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Every control in the header row - the mode toggle's links, the period and
   comparison selects - shares one fixed height (2026-09-11). Before this a
   native <select> measured 35px tall against the mode toggle's 21px link,
   each vertically centering independently inside the flex row: nothing lined
   up, and no amount of gap/padding tweaking fixes a height mismatch. */
.mode-toggle a,
.period-picker select,
.banding-picker select,
.period-picker button {
  box-sizing: border-box;
  height: 32px;
  min-height: 0;   /* else the global button's 44px min stretches the button */
  display: inline-flex;
  align-items: center;
}

/* The weekly/monthly meeting-view toggle. Same pill language as .delta so it
   reads as part of the header, not a new control style. */
.mode-toggle { display: flex; gap: 2px; padding: 2px; border: 1px solid var(--line); border-radius: 999px; }
.mode-toggle a {
  padding: 0 12px;
  border-radius: 999px;
  font-size: 13px;
  color: var(--muted);
  text-decoration: none;
}
.mode-toggle a.active { background: var(--ink); color: #fff; }
/* Same pill as a chart-view switch inside a modal chart's title row (§2). */
.mode-toggle button {
  height: 20px; min-height: 0; padding: 0 8px; border: 0; background: none; white-space: nowrap;
  font: inherit; font-size: 11px; line-height: 1; color: var(--muted); cursor: pointer;
}
.mode-toggle button[aria-pressed="true"] { background: var(--ink); color: #fff; }
.modal-chart-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.modal-chart-head .mode-toggle { flex-shrink: 0; padding: 1px; }

/* Monthly-only period picker, same pill language as .mode-toggle so the two
   read as one control group in the header. Bandingkan is the same control. */
.period-picker select,
.banding-picker select {
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 13px;
  color: var(--ink);
  background: transparent;
}
/* The noscript fallback button inherits no style by default and would render
   as the platform's default chrome - ugly next to the pill controls. Match
   the same border/radius/color language so it never reads as a different
   control if it ever shows. */
.period-picker button {
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 13px;
  color: var(--ink);
  background: #fff;
  cursor: pointer;
}

/* iOS Safari auto-zooms the viewport on focus when a form control's computed
   font-size is below 16px. The page is server-rendered, not a touch-first
   dashboard, so the desktop font can stay compact — but on screens where
   this control might be touched, bump it.

   A phone width alone was too narrow a guard. The one-screen 4-column layout
   is the only context where the header stays compact, and it is gated on
   `not (pointer: coarse)`; everywhere else — a 768px iPad portrait, a 1024px
   iPad landscape, a touch laptop — the pill renders at the 13px compact size
   and tapping "Periode" zooms the viewport. Coarse pointer is the real trigger
   for iOS zoom, so target it in addition to the existing phone floor; a
   fine-pointer desktop window does not zoom and stays compact. */
@media (max-width: 640px), (pointer: coarse) {
  .mode-toggle a,
  .period-picker select,
  .banding-picker select,
  .period-picker button { font-size: 16px; }
}

/* §2/§3 have no weekly-grain source (CompanyDashboardQuery) - dimmed, not
   hidden, so the real monthly figure underneath stays legible rather than
   needing its own placeholder state. §4 joins them in weekly mode. */
.dash-inactive { opacity: 0.4; pointer-events: none; }

/* Two columns on a desktop or projector, one on a phone. Cards are equal-width
   and independent; nothing reflows across the gap. */
.grid {
  display: grid;
  gap: 16px;
  grid-template-columns: repeat(auto-fit, minmax(420px, 1fr));
  align-items: start;
}
@media (max-width: 540px) {
  /* minmax(420px,...) would overflow a 360px phone before auto-fit collapses. */
  .grid { grid-template-columns: minmax(0, 1fr); }
}
.dash-card { max-width: none; padding: 20px; }
/* Swiss flatten (2026-09-10): the KPI figures are label-over-value blocks
   separated by whitespace, not filled boxes inside the card. No box-in-box.
   The §1/§2 rails and §5's 2x2 keep their own hairline dividers (own blocks). */
.dash-card .tiles { margin-bottom: 16px; gap: 12px 28px; }
.dash-card .tiles .tile { padding: 0; background: none; border: 0; }
.dash-card .tile-value { font-size: clamp(19px, 1.7vw, 26px); }
/* Full width whatever the column count is — a leaderboard of eleven bars is
   unreadable in a third of a screen. */
.span-all { grid-column: 1 / -1; }

.dash-card h2 {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 16px;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);
}
/* §3–§8 are the same "small box" tier: 18px titles with no number badge, so the
   six headers line up across the row. */
.dash .grid > [data-section="3"] > h2,
.dash .grid > [data-section="4"] > h2,
.dash .grid > [data-section="5"] > h2,
.dash .grid > [data-section="6"] > h2,
.dash .grid > [data-section="7"] > h2,
.dash .grid > [data-section="8"] > h2 { font-size: 18px; }
.dash .grid > [data-section="3"] > h2 .num,
.dash .grid > [data-section="4"] > h2 .num,
.dash .grid > [data-section="5"] > h2 .num,
.dash .grid > [data-section="6"] > h2 .num,
.dash .grid > [data-section="7"] > h2 .num,
.dash .grid > [data-section="8"] > h2 .num { display: none; }
/* §5 carries four equal KPI tiles (Occupation / Kehadiran / SQ Score / Kuota) —
   always 2x2, never a single wide row, and the target sits on its own line under
   the value rather than wrapping mid-figure. */
/* Overrides the one-screen block's `grid-auto-flow: column` (as-many-cols-as-tiles):
   §5's four tiles wrap 2x2 instead of a single cramped four-across row. */
.dash .grid > [data-section="5"] > .tiles {
  grid-auto-flow: row;
  grid-template-columns: 1fr 1fr;
  /* A hairline 2x2 grid — the four ratios read as a scorecard, not four loose
     numbers, now that the tile fills are gone. */
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
}
/* A ratio SQ has not keyed shows a dash where the figure would be — label
   stays put, only the dash is centred in the tile's body. */
.dash .grid > [data-section="5"] .tile-kosong .tile-value {
  flex: 1; display: flex; align-items: center; justify-content: center;
}
.dash .grid > [data-section="5"] .tile-empty {
  color: var(--muted); font-weight: 400; letter-spacing: 0;
}
.dash .grid > [data-section="5"] .tiles .tile {
  background: var(--field);
  padding: 12px 14px;
}
/* "48,6%" big; "/ 70,0%" muted on its own line under it so the headline figure
   has the tile's full width to grow into. */
.dash .grid > [data-section="5"] .tile-den {
  display: block; font-weight: 500; color: var(--muted);
  font-size: 0.42em; white-space: nowrap; margin-top: 0.15em;
}
/* §6 carries four KPI tiles (Total Job / CH per Barber / Barber Aktif /
   % Request Barber) — a hairline 2x2 scorecard, identical treatment to §5, so the
   four figures read as a grid, not four loose numbers floating in the card.
   Unconditional so it also beats the one-screen block's `grid-auto-flow: column`.
   §7 (Total / New Barbertologian / Retention / Customer Moving, chart dropped
   2026-09-11) and §8 (IN / OUT / Transfer / Turnover Rate, chart dropped
   2026-09-22 — the trend moved into the modal-only view) get the same
   treatment. */
.dash .grid > [data-section="4"] > .tiles,
.dash .grid > [data-section="6"] > .tiles,
.dash .grid > [data-section="7"] > .tiles,
.dash .grid > [data-section="8"] > .tiles {
  grid-auto-flow: row;
  grid-template-columns: 1fr 1fr;
  grid-auto-rows: 1fr; /* equal rows — the two baselines line up across the 2x2 */
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
}
.dash .grid > [data-section="4"] .tiles .tile,
.dash .grid > [data-section="6"] .tiles .tile,
.dash .grid > [data-section="7"] .tiles .tile,
.dash .grid > [data-section="8"] .tiles .tile {
  background: var(--field);
  padding: 12px 14px;
}
/* §3's single net-profit headline tile sits centred in the card. */
.dash .grid > [data-section="3"] .np-head { text-align: center; }
/* Over budget = red. */
.dash .grid > [data-section="4"] .np-div.over .tile-value > span:first-child,
.dash .grid > [data-section="4"] .np-div.over .tile-foot { color: #a3301a; font-weight: 600; }
/* Haircut / Barber target trails the value on the same line — "45 / 75". §3's
   net profit and §4's divisi budgets read the same way. */
.dash .grid > [data-section="3"] .tile-den,
.dash .grid > [data-section="4"] .tile-den,
.dash .grid > [data-section="6"] .tile-den {
  display: inline; font-weight: 500; color: var(--muted);
  font-size: 0.5em; white-space: nowrap;
}
.dash-card h2 .num {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  flex: 0 0 24px;
  /* The badges are this product's signature on the page. They earned the
     brand color already; the missing half of the look is a soft brand tint
     BEHIND the brand number — a hairline ring of the same hue a few shades
     up. The number sits in a field that says "Barbertology owns this," not
     "an ordered list got rendered." The radius matches the card itself
     (var(--radius)) on purpose: badges look like part of the card, not a
     foreign sticker. */
  background: #f3e4dc;
  color: var(--brand);
  border: 1px solid #e8d0c2;
  border-radius: 8px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
}

.tile-foot { display: block; margin-top: 4px; font-size: 12px; color: var(--muted); }
/* §1 lead line: mark a prior month that the current one fell short of — the
   header carries the MoM %, this just puts the direction on the figure itself. */
.tile-foot.is-down::before { content: "\25BE\00A0"; color: #a3301a; }
/* Delta-first compare foot (§3–§8, ?bandingkan= mode). Only rendered when the
   move is material, so it is meant to catch the eye. Arrow follows the sign
   (.up/.down); colour follows whether the move is good for the business
   (.baik green / .buruk red) — the two can disagree, e.g. turnover rate up. */
.tile-foot.cmp { font-weight: 600; color: var(--ink); }
.tile-foot.cmp::before { font-size: 9px; }
.tile-foot.cmp.up::before { content: "\25B2\00A0"; }
.tile-foot.cmp.down::before { content: "\25BE\00A0"; }
.tile-foot.cmp.baik { color: #1e6b33; }
.tile-foot.cmp.buruk { color: #a3301a; }
.tile-foot.cmp .cmp-vs { font-weight: 400; color: var(--muted); }
.chart-cap { margin: 0 0 8px; font-size: 13px; font-weight: 600; color: var(--muted); }
.chart-foot { margin: 8px 0 0; font-size: 12px; color: var(--muted); }

/* §1 used to carry a bespoke `.headline` band here (an outsized revenue number
   with no tile box and its own --vh scale). It now uses the shared
   `.tiles`/`.tile` chrome like every other card — see templates/dashboard/company.html §1. */
.chart-foot strong { font-variant-numeric: tabular-nums; }
.muted { color: var(--muted); }

/* --- meters: one value against one target, no axis ---------------------- */
.meter {
  position: relative;
  display: block;
  height: 10px;
  margin-top: 8px;
  background: var(--line-soft);
  border-radius: 999px;
  overflow: hidden;
}
.meter-lg { height: 16px; margin: 4px 0 0; overflow: visible; }
/* A mark overhangs the track by 3px top and bottom, so a meter carrying one
   cannot clip. Keyed on the mark itself rather than on a modifier class: the
   overflow is a consequence of the content, and §6's small meter needed it the
   moment it gained a prior-period mark. */
.meter:has(.meter-mark) { overflow: visible; }
.meter-fill {
  display: block;
  height: 100%;
  background: var(--s1);
  border-radius: 999px;
}
.meter-fill.alt { background: var(--s3); }
/* The pace/target marker. Drawn over the fill, so "behind schedule" is a gap
   you see rather than two percentages you subtract. */
.meter-mark {
  position: absolute;
  top: -3px;
  bottom: -3px;
  width: 3px;
  margin-left: -1px;
  background: var(--ink);
  border-radius: 2px;
}
.meter-legend {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin: 8px 0 0;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

.note {
  margin: 12px 0 0;
  padding: 10px 12px;
  background: #fdf6e6;
  border: 1px solid #ecd9a8;
  border-radius: var(--radius);
  font-size: 13px;
  line-height: 1.45;
}

.placeholder {
  display: grid;
  gap: 4px;
  margin-top: 16px;
  padding: 14px;
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  font-size: 13px;
  color: var(--muted);
}
.placeholder strong { color: var(--ink); font-size: 13px; }

/* --- legend: required for two series or more --------------------------- */
.legend { display: grid; gap: 6px; margin: 0; padding: 0; list-style: none; font-size: 13px; }
.legend-row { display: flex; flex-wrap: wrap; gap: 6px 18px; margin-top: 10px; }
.legend li { display: flex; align-items: center; gap: 8px; }
.legend strong { margin-left: auto; font-variant-numeric: tabular-nums; }
.legend-row strong { margin-left: 0; }
/* Legend share (d.kategoriPersen): five of the six §1 bands are single-percent
   slivers at card height, so the legend carries the mix the area can't show.
   This is the card's most informative figure, so it reads as real type, not as
   a muted annotation: full size, tabular, and ink-weight (not the muted
   secondary colour that under-sold it) so it holds up beside the label. */
.legend-row .pct { color: var(--ink); font-size: 1em; font-weight: 600; font-variant-numeric: tabular-nums; }
.swatch { width: 12px; height: 12px; flex: 0 0 12px; border-radius: 3px; background: var(--muted); }
/* k- prefix: these come from a loop index, i.e. from data (see docs/STATE.md).
   The slot numbers must match the SERIES array order in charts.js — the marks
   are painted there and the swatches here, and a mismatch makes the legend lie
   without erroring. */
.k-s1 { background: var(--s1); }
.k-s2 { background: var(--s2); }
.k-s3 { background: var(--s3); }
.k-s4 { background: var(--s4); }
.k-s5 { background: var(--s5); }
.k-s6 { background: var(--s6); }

/* The column is a sliver; the legend gets everything else. The band only has to
   be wide enough to read three stacked segments, while each legend row carries
   four figures (label, count, share, change) — a 0.8fr/1.2fr split starved the
   side that had more to say and clipped the delta chips at the card's edge.
   A fixed 64px rather than a fraction: the band's job does not get easier on a
   wider card, so extra width belongs to the legend at every size. */
/* container-type, not a viewport media query: this card sits in a grid whose
   own width depends on the sidebar and how many cards share a row, so a
   viewport breakpoint can stay "desktop" while this card itself is phone-
   narrow (found live 2026-09-07: sidebar expanded shrank the card to ~150px
   at a 1280px viewport, and the legend's fixed em tracks don't shrink - they
   just overflowed past the card edge, unreadable). Querying the card's own
   inline size catches that case the same way it catches an actual phone. */
.mix { display: grid; gap: 10px; grid-template-columns: 64px minmax(0, 1fr); align-items: center; container-type: inline-size; }
@container (max-width: 340px) { .mix { grid-template-columns: minmax(0, 1fr); } }
/* The legend reads in PAINT order, top to bottom, and what that means depends on
   which way the band runs — which is why this rule has been added, removed and
   added again as §6 changed shape. A vertical stack paints its first slice at
   the BOTTOM, so a top-down legend left in DOM order points at the slices upside
   down. `order` flips the visual rows to match the column while leaving the DOM
   alone, so reading and tab order stay in data order, and it keeps `.legend` as
   `display: grid` (a flex column-reverse fights the viewport-mode
   `align-items: stretch` further down and clumps every row against the bottom).
   Swatch colour stays tied to each row's own loop index, not its new visual
   position, so k-s1..k-s6 still point at the right slice.
   If this card ever goes horizontal again, delete these six rules — left-to-
   right already matches a top-down legend. */
.mix .legend li:has(.k-s1) { order: 6; }
.mix .legend li:has(.k-s2) { order: 5; }
.mix .legend li:has(.k-s3) { order: 4; }
.mix .legend li:has(.k-s4) { order: 3; }
.mix .legend li:has(.k-s5) { order: 2; }
.mix .legend li:has(.k-s6) { order: 1; }

/* This legend is a values TABLE — swatch, service, job count — so it is laid
   out as one, same reasoning as before (git blame for the fuller history).
   Share% and the vs-prior-period delta were both dropped (2026-09-07): the
   count is the figure asked for, and dropping the other two columns is what
   lets the label stay full-width instead of fighting for room in a ~220-300px
   card. The label track is `minmax(0, 1fr)` with ellipsis, not a fixed em
   width - it is the one column that can safely lose width before anything
   breaks, so it is the one that flexes; count stays a fixed em track (each
   `<li>` is its own grid, so only fixed-length tracks align across rows). */
.mix .legend {
  gap: 4px;
  font-size: 12px;
}
.mix .legend li {
  display: grid;
  grid-template-columns: 12px minmax(0, 1fr) 4.2em;
  column-gap: 8px;
  align-items: center;
}
.mix .legend li > span:not(.swatch) {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.mix .legend li > strong { text-align: right; }

.pair { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); margin-top: 16px; }
.pair strong { font-size: 18px; font-variant-numeric: tabular-nums; }

/* --- outlet chips ------------------------------------------------------- */
.chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0 0; }
.chip {
  padding: 4px 10px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 12px;
}
.chip-new { background: #e6f4f2; border-color: var(--s2); color: #0b5f57; font-weight: 600; }

/* Charts own their box; the axis labels are measured from the rendered text, so
   the host needs a real width to measure against on the first frame. */
[data-chart] { width: 100%; min-height: 60px; }
[data-chart] svg { display: block; overflow: visible; }


/* --- Dashboard Company: one screen, no fold ------------------------------
   The ten sections are read together — on a laptop, on a projector, in a
   meeting where nobody scrolls for you. So on a wide screen the page is
   exactly one viewport tall and the grid is a fixed 4 x 3 of cells: §9 and §10
   take two columns each on the last row, which is what makes 10 sections land
   in 12 slots. Nothing is below the fold because there is no fold, at 720p or
   at 1080p or at anything between.

   Two rules to keep in mind when editing a card:

   - Every card clips (`min-height: 0` + `overflow: hidden`). A card whose text
     outgrows its cell scrolls inside itself; the PAGE never scrolls. That is
     the guarantee, and it holds no matter what a later card puts in itself.
   - Type and spacing are in `--vh`, not px, so 720p and 1080p differ in scale
     rather than in what is on screen. `--vh` divides by `--z` for the same
     reason every other viewport length in this sheet does: `zoom` scales the
     paint, not the unit (see the note at the top of the file).

   Charts get whatever the card has left, so their height is measured at
   runtime rather than declared — `heightOf` in src/main/frontend/charts.js.
   The `data-height` attributes in the template are the fallback for the OTHER
   branch of this query, not dead values.

   Below 1000px, or on a touch device, none of this applies and the page scrolls
   as it did: ten cards on a phone screen is ten unreadable slivers. Touch
   answers here for the same reason it opts out of `--z` above — a finger and a
   projector want opposite things.

   Written as an opt-out for the same reason `--z` is, and it is not theoretical:
   a device with no pointing device at all reports `pointer: none`, matches
   neither `fine` nor `coarse`, and `and (pointer: fine)` silently drops it into
   the phone layout on a 1080p screen. Headless Chrome is one such device, which
   is how this was found. */
@media (min-width: 1000px) and (not (pointer: coarse)) {

  /* `.page.dash`, not `.dash`: Laba Rugi's `.page.wide` caps the column at
     1600px and would otherwise win on specificity, leaving a fifth of a 1080p
     screen empty on either side of a layout whose whole point is that it uses
     the screen it is given. */
  .page.dash {
    --vh: calc(1vh / var(--z));
    --pad: calc(1.1 * var(--vh));
    /* Type scale (2026-09-29): five steps, floor 12px. Replaced ~60 one-off
       clamps (floors down to 7px) — every size on this page is one of these. */
    --fs-1: clamp(12px, calc(1.25 * var(--vh)), 13px);  /* labels, captions, feet */
    --fs-2: clamp(13px, calc(1.7 * var(--vh)), 16px);   /* rows, table figures */
    --fs-3: clamp(16px, calc(2.4 * var(--vh)), 24px);   /* card titles, KPI values */
    --fs-4: clamp(20px, calc(3 * var(--vh)), 34px);     /* lead KPI */
    display: flex;
    flex-direction: column;
    gap: var(--pad);
    height: calc(100vh / var(--z));
    max-width: none;
    padding: var(--pad);
    overflow: hidden;
  }
  /* .nav-off pushes .page down by 56px to clear the fixed nav button. Here that
     would spend 56px of the one screen on empty space, so the banner steps
     around the button sideways instead. */
  .nav-off .dash { padding-top: var(--pad); }

  .dash .flash.err {
    margin: 0;
    padding: calc(0.45 * var(--vh)) 10px;
    font-size: var(--fs-1);
  }
  /* After the reset above, not before it: both selectors weigh the same, so the
     later one is the one that survives. */
  .nav-off .dash > .flash { margin-left: 46px; }

  /* One-screen mode is height-critical — the masthead hairline and its padding
     belong to the scrolling layout only. */
  .dash-head { align-items: start; gap: calc(0.5 * var(--vh)) 20px; margin: 0; padding-bottom: 0; border-bottom: 0; }
  /* The H1 fills the header band: 4*--vh scales it to the page's name at every
     viewport height. Cap 44px, floor 24px so it stays a title, not a banner or
     a body heading. Title cell is align-self:center so it sits centred against
     the two-row delta/scope column. */
  .dash-head h1 { margin: 0; font-size: clamp(24px, calc(4 * var(--vh)), 44px); }
  .dash-head .scope-line { font-size: var(--fs-2); }
  .dash-head-titles { min-width: 0; }
  .delta { padding: calc(0.3 * var(--vh)) 10px; font-size: var(--fs-1); }
  .delta strong { font-size: var(--fs-2); }
  .controls-label { font-size: var(--fs-1); }
  .mode-toggle a { padding: calc(0.3 * var(--vh)) 10px; font-size: var(--fs-1); }
  .period-picker select, .banding-picker select { padding: calc(0.3 * var(--vh)) 10px; font-size: var(--fs-1); }

  .dash .grid {
    flex: 1;
    min-height: 0;
    gap: var(--pad);
    /* Boss note 2026-08-31: three rows. Row 1 is §1 Revenue and §2 YTD in two
       wide columns; row 2 is §9 Outlet Leaderboard and §10 Barbertologist
       Leaderboard, likewise; row 3 is the six small boxes — §3–§8 — one per
       column. Six columns: mains span 3, smalls 1.
       Source order in company.html stays 1..10 (reading/tab/screen-reader
       order must match the numbering), so each card is placed explicitly
       rather than left to auto-flow, which would interleave 3–8 between the
       mains. */
    grid-template-columns: repeat(6, minmax(0, 1fr));
    /* The mains carry the page's headline charts and the leaderboards carry
       eleven and ten bars against small number cards, so the first two rows
       are given the extra. Row 2 (the two leaderboards) is given a touch more
       than row 1 — eleven / ten labelled bars need the vertical room more than
       §1/§2's rail-plus-chart faces do — with the smalls in row 3 giving it up
       (they are numbers-only and were the loosest of the three). */
    grid-template-rows: minmax(0, 1.28fr) minmax(0, 1.42fr) minmax(0, 0.95fr);
    /* NOT `start`, which the scrolling layout uses. A card that sizes to its own
       content has no height to give its chart, and an SVG sized `height: 100%`
       against an auto-height parent falls back to its viewBox ratio — which is
       computed from the height we just handed it. That is a loop, and it grows
       by 1/--z per frame until the card is millions of pixels tall. Stretching
       the card is what makes its height definite and closes it. */
    align-items: stretch;
  }
  /* Main rows: §1/§2 on top, §9/§10 beneath, three columns each. */
  .dash .grid > [data-section="1"]  { grid-area: 1 / 1 / 2 / 4; }
  .dash .grid > [data-section="2"]  { grid-area: 1 / 4 / 2 / 7; }
  .dash .grid > [data-section="9"]  { grid-area: 2 / 1 / 3 / 4; }
  .dash .grid > [data-section="10"] { grid-area: 2 / 4 / 3 / 7; }
  /* Small row: §3–§8, one per column. */
  .dash .grid > [data-section="3"]  { grid-area: 3 / 1 / 4 / 2; }
  .dash .grid > [data-section="4"]  { grid-area: 3 / 2 / 4 / 3; }
  .dash .grid > [data-section="5"]  { grid-area: 3 / 3 / 4 / 4; }
  .dash .grid > [data-section="6"]  { grid-area: 3 / 4 / 4 / 5; }
  .dash .grid > [data-section="7"]  { grid-area: 3 / 5 / 4 / 6; }
  .dash .grid > [data-section="8"]  { grid-area: 3 / 6 / 4 / 7; }
  /* Boss note 2026-09-01, revised 2026-09-02: §1 rehaul — the one-screen monthly
     card face carries the per-product target scorecard ([data-s1-target], filled
     by charts.js) in place of the revenue chart. Laid out as ONE column, top to
     bottom: h2 · the target progress bar and its caption · the lead revenue line
     · the scorecard filling the rest. The earlier two-column version put two
     figures in a narrow left rail against a six-row table and read lopsided —
     the rail sat half-empty while the table's bars were pinched. Single column
     gives the bars the card's full width and kills the imbalance.
     The chart, its caption, the axis/shadow foot notes and the six-category
     swatch legend all go (display:none, so they take no grid cell); `>` so the
     scorecard's own .chart-cap nested under [data-s1-target] survives. The chart
     host stays in the DOM for the §1 modal to clone.
     `:has(> [data-s1-target])` — charts.js removes that host in weekly mode
     (no per-product target for a week), so this only applies when the scorecard
     is actually present; weekly falls back to the stacked flex card.
     (Selector was missing the `:has()` scope itself until 2026-09-06 - it hid
     the chart unconditionally, so weekly mode showed neither the scorecard
     nor the chart it was supposed to fall back to: a blank card.) */
  .dash .grid > [data-section="1"]:has(> [data-s1-target]) > :is([data-chart], .chart-cap, .chart-foot, .legend) { display: none; }
  /* §1 one-screen monthly face (redesign 2026-09-02, "rail + scorecard"): the
     title, then two columns — a left rail carrying Total Revenue and % dari
     Target as big brand figures, and the per-product target scorecard filling
     the rest. `:has(> [data-s1-target])` so this fires only when the scorecard
     is present (charts.js drops that host in weekly mode → narrow fallback). */
  .dash .grid > [data-section="1"]:has(> [data-s1-target]) {
    display: grid;
    grid-template-columns: minmax(168px, 0.9fr) minmax(0, 2.6fr);
    grid-template-rows: auto minmax(0, 1fr);
    column-gap: calc(1.6 * var(--vh));
    row-gap: calc(0.6 * var(--vh));
  }
  /* §1 face carries a larger title and drops the "1" badge — the card is the
     page's lead figure, not one item in a numbered list. Source order is
     untouched (screen-reader / tab order still 1..10). */
  .dash .grid > [data-section="1"]:has(> [data-s1-target]) > h2 {
    grid-area: 1 / 1 / 2 / -1;
    font-size: var(--fs-3);
    letter-spacing: 0.04em; margin-bottom: 0;
  }
  .dash .grid > [data-section="1"]:has(> [data-s1-target]) > h2 .num { display: none; }

  /* The full-width revenue-vs-target bar is off on this face (boss call
     2026-09-02) — the % lives big in the rail, the deep-dive is in the modal. */
  .dash .grid > [data-section="1"]:has(> [data-s1-target]) > .s1-progress-wrap { display: none; }

  /* Left rail: Total Revenue over % dari Target, each a small uppercase label
     above a big brand figure, split from the scorecard by a vertical rule. Both
     tiles show here (the narrow layout still owns the meter). */
  .dash .grid > [data-section="1"]:has(> [data-s1-target]) > .tiles {
    grid-area: 2 / 1 / 3 / 2;
    margin: 0; padding: 0 calc(1.5 * var(--vh)) 0 0; border: 0;
    border-right: 1px solid var(--line);
    display: flex; flex-direction: column;
    gap: 0;
  }
  /* Each rail figure is its own equal-height segment, its content centred in it,
     with a hairline between segments. */
  .dash .grid > [data-section="1"]:has(> [data-s1-target]) .tiles .tile {
    flex: 1 1 0;
    display: flex; flex-direction: column; justify-content: center; gap: calc(0.3 * var(--vh));
    padding: 0.6em 0; background: none; border: 0;
  }
  .dash .grid > [data-section="1"]:has(> [data-s1-target]) .tiles .tile + .tile {
    border-top: 1px solid var(--line);
  }
  .dash .grid > [data-section="1"]:has(> [data-s1-target]) .tile-label {
    margin: 0; letter-spacing: 0.03em; text-transform: uppercase; color: var(--muted);
    font-size: var(--fs-1);
  }
  /* nowrap: the figure ("Rp 1,2 M", "62,3%") is one token and must never break
     across lines — in the narrow rail (worse in fullscreen, taller viewport →
     clamp at its cap) that wrap overflowed and clipped against the card's
     `overflow: hidden`. The denominator is `inline-block` so it drops to the
     next line as a whole unit when there's no room, and trails inline when
     there is. §2's YTD rail shares this exact treatment. */
  .dash .grid > [data-section="1"]:has(> [data-s1-target]) .tile-value {
    color: var(--brand); font-weight: 700; line-height: 1.04; letter-spacing: -0.01em;
    /* Cap dropped 33->26 / mid 3->2.4 vh: the miliar figures carry three
       decimals now ("Rp 1,200 M" / "Rp 13,000 M"), and at the old size the
       nowrap token overflowed the min-width rail. §2 keeps this in step. */
    font-size: var(--fs-3);
    white-space: nowrap;
  }
  /* Revenue / % go red when the month is short of target (same negative colour
     as the gap figure). The muted denominator span keeps its own colour. */
  .dash .grid > [data-section="1"]:has(> [data-s1-target]) .tile-value.neg { color: #a3301a; }
  /* "/ target" denominator trailing each rail figure — muted, lighter, ~half the
     figure's size, sitting on its baseline. */
  .dash .grid > [data-section="1"]:has(> [data-s1-target]) .tile-den {
    display: inline-block; color: var(--muted); font-weight: 500;
    font-size: max(12px, 0.55em); letter-spacing: 0; white-space: nowrap;
  }
  /* The gap is its own rail segment (boss call): a "Kurang" / "Lewat" label, then
     the rupiah figure at the same size as the %. */
  .dash .grid > [data-section="1"]:has(> [data-s1-target]) .tile-gap {
    line-height: 1.1; font-variant-numeric: tabular-nums;
  }
  .dash .grid > [data-section="1"]:has(> [data-s1-target]) .tile-foot {
    display: block; margin: calc(0.2 * var(--vh)) 0 0; color: var(--muted);
    font-size: var(--fs-1); font-variant-numeric: tabular-nums;
  }

  /* Scorecard fills the right column. */
  .dash .grid > [data-section="1"]:has(> [data-s1-target]) > [data-s1-target] {
    grid-area: 2 / 2 / 3 / -1; align-self: stretch;
    display: flex; flex-direction: column; min-height: 0;
    gap: calc(0.15 * var(--vh));
  }
  /* Caption sits right on the table — no inherited chart-cap margin. */
  .dash .grid > [data-section="1"] [data-s1-target] .chart-cap { margin: 0; }
  /* Per-product target rupiah foot is modal-only; dropping it buys the rows height. */
  .dash .grid > [data-section="1"] [data-s1-target] .chart-foot { display: none; }
  /* Header sizes to its text; the five product rows split the rest evenly so bar
     height, type and gaps all scale with the card. `min-height: 0` lets the 1fr
     rows shrink on a short viewport rather than overflow. */
  .dash .grid > [data-section="1"] .k-bars {
    font-size: var(--fs-2); gap: calc(0.5 * var(--vh)) 12px;
    flex: 1 1 0; min-height: 0;
    grid-template-rows: auto repeat(5, minmax(0, 1fr));
  }
  .dash .grid > [data-section="1"] .k-bar-track,
  .dash .grid > [data-section="1"] .k-div-track { height: clamp(12px, calc(2 * var(--vh)), 20px); }
  /* Four columns on the face now, matching the modal: name · bar · Aktual ·
     Selisih. %Target dropped (2026-09-16) — the bar's own split carries it. */
  .dash .grid > [data-section="1"] .k-bars.k-target {
    grid-template-columns: max-content minmax(120px, 1fr) 3.8em 5em 5.6em;
  }
  /* Compare mode: card face is name · bar · one Δ column at value size. */
  .dash .grid > [data-section="1"] .k-bars.k-target.has-band.band-compact {
    grid-template-columns: max-content minmax(120px, 1fr) 6.5em;
  }
  .dash .grid > [data-section="1"] .band-compact .k-bar-row:not(.k-bar-head) .k-bar-band {
    font-size: var(--fs-2);
  }
  .dash .grid > [data-section="1"] .k-bar-head { font-size: var(--fs-1); }
  /* :not(.k-bar-head) — the header's own SELISIH label stays header-sized. */
  .dash .grid > [data-section="1"] .k-bar-row:not(.k-bar-head) .k-bar-shift {
    font-size: var(--fs-2);
  }
  /* No left inset edge on the card-face scorecard rows — every product misses
     this aspirational target so it lit every row, reading as noise. The row tint
     carries the pass/fail on its own here; the §1 modal keeps the edge. */
  .dash .grid > [data-section="1"] .k-bar-row:is(.is-bad, .is-good, .is-watch) {
    padding-block: 1px; box-shadow: none; background: none;
  }
  /* No category dot on the card face — the stacked bar and legend it keyed to
     are hidden here, so it was colour without a referent. The §1 modal keeps
     its dots (the stacked bar lives there). */
  .dash .grid > [data-section="1"] .k-target .k-dot { display: none; }
  .dash .grid > [data-section="1"] .t-bar-track::after { opacity: 0.5; width: 2px; }

  /* §2 chases §1's one-screen face (boss call 2026-09-02): a left rail of big
     brand figures (Realisasi YTD · % target · Proyeksi akhir tahun) over the
     cumulative YTD chart in the right column, split by a vertical rule. Gated
     on the chart host, which Thymeleaf renders only in bulanan mode — weekly
     has no host and falls back to the stacked flex card. Source order stays 2. */
  /* Same rail geometry as §1's one-screen face — keep these in step. */
  .dash .grid > [data-section="2"]:has(> [data-chart="ytd"]) {
    display: grid;
    grid-template-columns: minmax(168px, 0.9fr) minmax(0, 2.6fr);
    grid-template-rows: auto minmax(0, 1fr);
    column-gap: calc(1.6 * var(--vh));
    row-gap: calc(0.6 * var(--vh));
  }
  .dash .grid > [data-section="2"]:has(> [data-chart="ytd"]) > h2 {
    grid-area: 1 / 1 / 2 / -1;
    font-size: var(--fs-3);
    letter-spacing: 0.04em; margin-bottom: 0;
  }
  .dash .grid > [data-section="2"]:has(> [data-chart="ytd"]) > h2 .num { display: none; }
  /* The pace meter, its caption and legend belong to the stacked layout — the
     two rail tiles carry tercapai and pace-ideal on this face. */
  .dash .grid > [data-section="2"]:has(> [data-chart="ytd"]) > :is(.chart-cap, .meter, .meter-legend) {
    display: none;
  }
  /* Left rail — same treatment as §1's: each figure its own equal-height
     segment, centred, brand colour, a hairline between. */
  .dash .grid > [data-section="2"]:has(> [data-chart="ytd"]) > .tiles {
    grid-area: 2 / 1 / 3 / 2;
    margin: 0; padding: 0 calc(1.5 * var(--vh)) 0 0; border: 0;
    border-right: 1px solid var(--line);
    display: flex; flex-direction: column; gap: 0;
  }
  .dash .grid > [data-section="2"]:has(> [data-chart="ytd"]) .tiles .tile {
    flex: 1 1 0;
    display: flex; flex-direction: column; justify-content: center; gap: calc(0.3 * var(--vh));
    padding: 0.6em 0; background: none; border: 0;
  }
  .dash .grid > [data-section="2"]:has(> [data-chart="ytd"]) .tiles .tile + .tile {
    border-top: 1px solid var(--line);
  }
  .dash .grid > [data-section="2"]:has(> [data-chart="ytd"]) .tile-label {
    margin: 0; letter-spacing: 0.03em; text-transform: uppercase; color: var(--muted);
    font-size: var(--fs-1);
  }
  /* Identical to §1's rail figure — nowrap token, inline-block denominator that
     drops as a unit when the rail can't hold it inline (see §1's note). */
  .dash .grid > [data-section="2"]:has(> [data-chart="ytd"]) .tile-value {
    color: var(--brand); font-weight: 700; line-height: 1.04; letter-spacing: -0.01em;
    /* In step with §1's rail — smaller cap so the 3-decimal miliar figures fit. */
    font-size: var(--fs-3);
    white-space: nowrap;
  }
  .dash .grid > [data-section="2"]:has(> [data-chart="ytd"]) .tile-value.neg { color: #a3301a; }
  .dash .grid > [data-section="2"]:has(> [data-chart="ytd"]) .tile-den {
    display: inline-block; color: var(--muted); font-weight: 500;
    font-size: max(12px, 0.55em); letter-spacing: 0; white-space: nowrap;
  }
  .dash .grid > [data-section="2"]:has(> [data-chart="ytd"]) .tile-foot {
    display: block; margin: calc(0.2 * var(--vh)) 0 0; color: var(--muted);
    font-size: var(--fs-1); font-variant-numeric: tabular-nums;
  }
  /* Chart fills the right column. */
  .dash .grid > [data-section="2"]:has(> [data-chart="ytd"]) > [data-chart="ytd"] {
    grid-area: 2 / 2 / 3 / -1; align-self: stretch; min-height: 0;
  }

  /* §9 one-screen face: title only, like §1/§2. The chart-cap ("Semua outlet
     terhadap target sendiri, <periode>") repeats what the h2 and the axis
     already say at meeting distance; it stays in the DOM as the §9 modal's
     data-label source. The chart-foot legend (dash = 100% target, rule =
     company average, full colour = top/bottom 3) stays — that encoding is not
     self-evident. */
  .dash .grid > [data-section="9"] > .chart-cap { display: none; }
  /* Title only, sized like §1/§2's one-screen face — the leaderboard reads as
     a headline, not item 9 in a list. Source order stays 9 (tab / screen-reader
     order untouched). */
  .dash .grid > [data-section="9"] > h2 {
    font-size: var(--fs-3);
    letter-spacing: 0.04em;
  }
  .dash .grid > [data-section="9"] > h2 .num { display: none; }

  /* §10 one-screen face: the two-column ranked list ([data-barber-strip], filled
     by charts.js) stands in for the 20-row Haircut/Package chart — 20 grouped
     bars can't fit this cell legibly. The chart host, its swatch legend and the
     chart-cap all hide (display:none, so no grid cell) but stay in the DOM for
     the §10 modal to clone / use as data-label. The strip's own column heads
     ("10 Teratas" / "10 Terbawah") already say what it is. Title sized like §9. */
  .dash .grid > [data-section="10"] > :is([data-chart], .legend, .chart-cap) { display: none; }
  .dash .grid > [data-section="10"] > h2 {
    font-size: var(--fs-3); letter-spacing: 0.04em;
  }
  .dash .grid > [data-section="10"] > h2 .num { display: none; }
  .dash .grid > [data-section="10"] { display: flex; flex-direction: column; }
  .dash .grid > [data-section="10"] > [data-barber-strip] {
    display: block; flex: 1; min-height: 0; overflow: hidden;
  }
  /* Compare mode: the dumbbell replaces the strip on the face — 20 dots+lines
     do fit this cell where 40 grouped bar-segments did not. */
  .dash .grid > [data-section="10"].cmp > [data-barber-strip] { display: none; }
  .dash .grid > [data-section="10"].cmp > [data-chart] {
    display: block; flex: 1; min-height: 0;
  }
  .dash .grid > [data-section="10"].cmp > .chart-cap { display: block; }
  .dash .grid > [data-section="10"].cmp > .chart-foot { display: none; }

  /* The six smalls — §3–§8 — are the page's compact class: real small cards
     (boss note 2026-08-31, "real real small"). Numbers / small charts only, so
     every intrinsic block is tightened one notch below the shared .dash-card
     scale and the chart's height floor drops, letting the mains' 1.4fr
     columns read as a step above rather than as an equal card. */
  .dash .grid > [data-section="3"],
  .dash .grid > [data-section="4"],
  .dash .grid > [data-section="5"],
  .dash .grid > [data-section="6"],
  .dash .grid > [data-section="7"],
  .dash .grid > [data-section="8"] {
    padding: calc(0.45 * var(--vh)) calc(0.55 * var(--vh));
  }
  .dash .grid > [data-section="3"] h2,
  .dash .grid > [data-section="4"] h2,
  .dash .grid > [data-section="5"] h2,
  .dash .grid > [data-section="6"] h2,
  .dash .grid > [data-section="7"] h2,
  .dash .grid > [data-section="8"] h2 {
    margin: 0 0 calc(0.3 * var(--vh));
    font-size: var(--fs-1);
  }
  .dash .grid > [data-section="3"] h2 .num,
  .dash .grid > [data-section="4"] h2 .num,
  .dash .grid > [data-section="5"] h2 .num,
  .dash .grid > [data-section="6"] h2 .num,
  .dash .grid > [data-section="7"] h2 .num,
  .dash .grid > [data-section="8"] h2 .num {
    width: 14px; height: 14px; flex: 0 0 14px; font-size: 8px;
  }
  /* §3–§8 titles run bigger than their siblings, no number badge (on request). */
  .dash .grid > [data-section="3"] h2,
  .dash .grid > [data-section="4"] h2,
  .dash .grid > [data-section="5"] h2,
  .dash .grid > [data-section="6"] h2,
  .dash .grid > [data-section="7"] h2,
  .dash .grid > [data-section="8"] h2 {
    font-size: var(--fs-2);
  }
  /* §5, §6, §7 and §8 have no chart to eat the card's height — the four tiles
     take it instead. The hairline 2x2 stretches to fill (`.dash-card` is
     flex-column), each tile centring its own stack, so the figures read as
     large as the box allows. */
  .dash .grid > [data-section="5"] > .tiles,
  .dash .grid > [data-section="6"] > .tiles,
  .dash .grid > [data-section="7"] > .tiles,
  .dash .grid > [data-section="8"] > .tiles {
    flex: 1 1 auto; min-height: 0;
    grid-template-rows: 1fr 1fr;
    /* keep the hairline 2x2 from the base rule — the shared §3–§8 gap below
       would otherwise flatten it back to a plain gap */
    gap: 1px;
  }
  .dash .grid > [data-section="5"] .tiles .tile {
    display: flex; flex-direction: column; justify-content: space-between;
    min-height: 0; padding: calc(0.7 * var(--vh)) calc(0.9 * var(--vh));
    background: var(--field);
  }
  /* §6, §7 and §8's tiles carry uneven child counts (a plain label+value tile
     next to one with a foot line) — space-between would slam the bare figures
     to the tile floor. Stack from the top instead: label, value right under
     it, any context below that. */
  .dash .grid > [data-section="6"] .tiles .tile,
  .dash .grid > [data-section="7"] .tiles .tile,
  .dash .grid > [data-section="8"] .tiles .tile {
    display: flex; flex-direction: column; justify-content: flex-start;
    gap: calc(0.15 * var(--vh));
    min-height: 0; padding: calc(0.7 * var(--vh)) calc(0.9 * var(--vh));
    background: var(--field);
  }
  .dash .grid > [data-section="3"] > .tiles {
    flex: 1 1 auto; min-height: 0;
    grid-auto-flow: row; grid-template-columns: 1fr;
    gap: 0;
  }
  .dash .grid > [data-section="3"] .tiles .tile {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    min-height: 0; padding: calc(0.7 * var(--vh)) calc(0.9 * var(--vh));
    background: var(--field); text-align: center;
  }
  .dash .grid > [data-section="3"] .tile-value {
    font-size: var(--fs-4); letter-spacing: -0.02em; line-height: 1.05;
  }
  .dash .grid > [data-section="3"] .tile-foot { font-size: var(--fs-1); }
  .dash .grid > [data-section="5"] .tiles,
  .dash .grid > [data-section="6"] .tiles,
  .dash .grid > [data-section="7"] .tiles,
  .dash .grid > [data-section="8"] .tiles { margin: 0 0 calc(0.3 * var(--vh)); }
  /* §4 is a chartless 2x2 of divisi budgets, like §5–§8: rows share the card's
     height, tiles stack from the top. */
  .dash .grid > [data-section="4"] > .tiles {
    flex: 1 1 auto; min-height: 0; gap: 1px; grid-auto-rows: 1fr;
  }
  .dash .grid > [data-section="4"] .tiles .tile {
    display: flex; flex-direction: column; justify-content: flex-start;
    gap: calc(0.1 * var(--vh));
    min-height: 0; padding: calc(0.55 * var(--vh)) calc(0.9 * var(--vh));
  }
  .dash .grid > [data-section="4"] .tile-value {
    font-size: var(--fs-3);
    letter-spacing: -0.02em; line-height: 1.05;
  }
  .dash .grid > [data-section="4"] .tile-foot { font-size: var(--fs-1); }
  /* §5, §6, §7 and §8 all carry a chartless 2x2 of four KPI tiles — the layout
     has the vertical room for all four figures to read as headlines. */
  .dash .grid > [data-section="5"] .tile-value,
  .dash .grid > [data-section="6"] .tile-value,
  .dash .grid > [data-section="7"] .tile-value,
  .dash .grid > [data-section="8"] .tile-value {
    font-size: var(--fs-4);
    letter-spacing: -0.02em; line-height: 1.05;
  }
  .dash .grid > [data-section="5"] .tile-label,
  .dash .grid > [data-section="6"] .tile-label,
  .dash .grid > [data-section="7"] .tile-label,
  .dash .grid > [data-section="8"] .tile-label {
    font-size: var(--fs-1);
  }
  .dash .grid > [data-section="5"] .tile-foot,
  .dash .grid > [data-section="6"] .tile-foot,
  .dash .grid > [data-section="7"] .tile-foot,
  .dash .grid > [data-section="8"] .tile-foot {
    font-size: var(--fs-1);
  }
  .dash .grid > [data-section="3"] .tile-label,
  .dash .grid > [data-section="4"] .tile-label,
  .dash .grid > [data-section="5"] .tile-label,
  .dash .grid > [data-section="6"] .tile-label,
  .dash .grid > [data-section="7"] .tile-label,
  .dash .grid > [data-section="8"] .tile-label,
  .dash .grid > [data-section="4"] .chart-cap,
  .dash .grid > [data-section="5"] .chart-cap,
  .dash .grid > [data-section="6"] .chart-cap,
  .dash .grid > [data-section="7"] .chart-cap,
  .dash .grid > [data-section="8"] .chart-cap,
  .dash .grid > [data-section="4"] .chart-foot,
  .dash .grid > [data-section="5"] .chart-foot,
  .dash .grid > [data-section="6"] .chart-foot,
  .dash .grid > [data-section="7"] .chart-foot,
  .dash .grid > [data-section="8"] .chart-foot {
    font-size: var(--fs-1);
  }
  .dash .grid > [data-section="4"] [data-chart],
  .dash .grid > [data-section="5"] [data-chart],
  .dash .grid > [data-section="6"] [data-chart],
  .dash .grid > [data-section="7"] [data-chart],
  .dash .grid > [data-section="8"] [data-chart] {
    min-height: calc(2.5 * var(--vh));
  }
  .dash .grid > [data-section="4"] .chips,
  .dash .grid > [data-section="5"] .chips,
  .dash .grid > [data-section="6"] .chips,
  .dash .grid > [data-section="7"] .chips,
  .dash .grid > [data-section="8"] .chips { font-size: var(--fs-1); }
  .dash .grid > [data-section="4"] .legend,
  .dash .grid > [data-section="5"] .legend,
  .dash .grid > [data-section="6"] .legend,
  .dash .grid > [data-section="7"] .legend,
  .dash .grid > [data-section="8"] .legend { font-size: var(--fs-1); }

  .dash-card {
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow: hidden;
    padding: calc(1 * var(--vh)) calc(1.1 * var(--vh));
  }
  .dash-card h2 {
    gap: 7px;
    margin: 0 0 calc(0.8 * var(--vh));
    font-size: var(--fs-1);
  }
  /* A title that cannot fit its column truncates rather than taking a second
     line out of the chart below it. */
  .dash-card h2 > span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .dash-card h2 .num {
    width: 19px;
    height: 19px;
    flex: 0 0 19px;
    border-radius: 6px;
    font-size: 10px;
  }

  /* auto-fit + minmax(200px) resolves to ONE column inside a quarter-width
     card, which stacks two tiles where the design has them side by side.
     Explicit column flow instead: as many columns as there are tiles, always. */
  .dash-card .tiles {
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    grid-template-columns: none;
    gap: calc(0.6 * var(--vh));
    margin: 0 0 calc(0.8 * var(--vh));
  }
  .dash-card .tiles:last-child { margin-bottom: 0; }
  .dash-card .tiles .tile { padding: calc(0.55 * var(--vh)) calc(0.75 * var(--vh)); }
  .dash-card .tile-label { margin-bottom: 2px; font-size: var(--fs-1); }
  /* 1.55, not 2: three tiles across a quarter-width card leave ~86px for the
     text, and the miliar figures ("Rp 18,000 M", three decimals now) wrap
     mid-figure at 720p one notch larger. */
  .dash-card .tile-value { font-size: var(--fs-3); }
  /* §2's YTD card is the year's balance at a glance, the second headline on the
     one-screen meeting view — so its leading figure (Revenue YTD) reads a step
     above its neighbours. §1's own rail already sizes its lead figures larger
     via the [data-s1-target] block, so it is deliberately excluded here. The
     lift is gentler than §1's: Revenue YTD is a long tabular string (longer
     still with three-decimal miliar) and must not wrap mid-figure. */
  .dash .grid > [data-section="2"] .tiles .tile:first-child .tile-value {
    font-size: var(--fs-3);
    letter-spacing: -0.02em;
  }
  /* On the one-screen face the rail figures are all one size (like §1's), so
     the stacked layout's first-child lift above is normalised back. */
  .dash .grid > [data-section="2"]:has(> [data-chart="ytd"]) .tiles .tile:first-child .tile-value {
    font-size: var(--fs-3);
    letter-spacing: -0.01em;
  }
  .dash-card .tile-foot { margin-top: 1px; font-size: var(--fs-1); }

  /* The chart-cap belongs to the chart, not to the gap above it: its bottom
     margin separates the caption from the chart it captions; the space above
     it is owned by the preceding .tiles' own bottom margin. */
  .dash-card .chart-cap { margin: 0 0 calc(0.35 * var(--vh)); font-size: var(--fs-1); }
  .dash-card .chart-foot { margin: calc(0.35 * var(--vh)) 0 0; font-size: var(--fs-1); }

  /* The one flexible box on a card. Everything else keeps its intrinsic height,
     so the chart is what gives way — and the floor stops it giving way to
     nothing. Basis 0, not auto: an auto basis is the chart's own content, and
     the chart's content is whatever height we last gave it.

     --chart-fill is read by charts.js, and it is the whole reason the flag
     exists rather than the media query being written out twice: it says "this
     box's height belongs to the layout, measure it" and it says so from the one
     place that decides that. Without it the chart is sized by `data-height`. */
  /* No `overflow: hidden` here, deliberately. The library caps the gutter it
     gives y-axis categories, so a long one ("Kuota Barbertologist" in a quarter
     of a 720p screen) is drawn a few pixels outside the viewBox — which is what
     `[data-chart] svg { overflow: visible }` above is for. The card's padding
     absorbs the spill, and the card clips, so the one-screen guarantee is still
     enforced one level up. */
  .dash-card [data-chart] { --chart-fill: 1; flex: 1 1 0; min-height: calc(6 * var(--vh)); }
  /* §2's YTD chart is the one card whose fixed content (tiles + meter +
     legend + foot) over-subscribes its cell at 720p/768p by a
     few px — the generic 6*--vh floor is what pushed it over. Its cumulative
     lines read fine shorter; the meter above carries the headline. */
  .dash-card [data-chart='ytd'] { min-height: calc(4 * var(--vh)); }
  /* Axis and value labels are the one text on a card that did NOT scale with
     --vh: they are painted by the library at a fixed size (see the
     `[data-chart] text` rule below). At 720p that is the same size inside a
     card two-thirds the height, which is what made §4's nine cost lines and
     §9/§10's ten-and-eleven row names collide into each other.

     The FLOOR is what matters here, and it is a physical measurement, not a
     CSS one. `body { zoom: 0.8 }` above means a CSS px paints at 0.8px, so the
     old clamp(8px … 11px) reached the eye as 6,4–8,8px — well under the 12px
     readability floor, on a page whose own comments say it is read off a
     meeting-room projector. 15px CSS is 12px painted; the 13px floor is 10,4px
     at 720p, which is the trade that keeps ten cards on one screen.

     Set on the HOST, not on `text`, and that placement is load-bearing: the
     renderer reserves each axis gutter from the label size it is TOLD (every
     axis passes `tickLabels.fontSize`), and charts.js resolves that number by
     reading this property off the host before any label exists. Sizing `text`
     directly instead is what clipped §9's and §10's outlet names off the left
     edge — CSS painted 15px labels into a gutter cut for 11px ones. */
  .dash-card [data-chart] { font-size: var(--fs-2); }
  /* §4 carries two charts. The trend is a shape, not a table - it keeps a
     sparkline's height so the cost matrix under it gets a row per line. */
  .dash-card [data-chart].spark { flex: 0 0 auto; height: clamp(22px, calc(3.2 * var(--vh)), 40px); min-height: 0; }
  /* Weekly mode drops the cost matrix under it (no weekly source), so the trend
     is the card's only chart and takes the room the matrix would have had
     rather than leaving a third of the card blank. */
  .dash-inactive [data-chart].spark { flex: 1 1 0; height: auto; min-height: calc(6 * var(--vh)); }
  .dash-card .mix { flex: 1 1 0; min-height: 0; align-items: stretch; gap: calc(0.9 * var(--vh)); }
  .dash-card .mix [data-chart] { min-height: 0; }

  /* Found live 2026-08-26 at 1512x694: six legend rows (Haircut..Layanan
     Lainnya) each carry a header-style `.delta` pill (border + padding +
     ~19px line height), needing ~123px total against the ~82px `.mix` had
     left to give — `.mix` has no overflow of its own, so the two rows that
     did not fit did not clip, they painted straight through into `.tiles`
     below (Package's count and Haircut's — the majority category at 66,6%
     of the period — both unreadable behind the % Permintaan Barber tile).
     Stripped to plain coloured text, the same treatment §1's own `.pct`
     already gets in this same legend family, and tightened to a single
     line: six rows now fit inside whatever `.mix` is given, no pill chrome
     to outgrow it. */
  /* Loosened from the 1px/1.1 this comment's own fix originally landed on -
     §6's tiles block dropped from two uneven boxes to one merged row the same
     day (2026-08-27), which gave `.mix` more of the card back. Re-verified at
     1512x694, the exact size the original crowding was caught at: still zero
     overflow on `.mix`/`.legend`/the card itself. */
  .dash-card .mix .legend { gap: 3px; }
  .dash-card .mix .legend li { line-height: 1.3; }
  .dash-card .mix .legend .delta { background: none; border: 0; padding: 0; border-radius: 0; }

  /* flex-shrink: 0 — found live 2026-08-23: at 1366x768/1280x720 the §2 meter
     collapsed to 0px tall. The card's flex column was over-subscribed (the
     chart's min-height floor refuses to give way), so flex squeezed the only
     item without a content floor — an empty-height bar — down to nothing,
     while .meter-mark (top/bottom -3px) kept painting outside the collapsed
     box: the user saw the target tick and no bar at all. Text items survive
     shrinking via min-height:auto's min-content floor; a meter has none. */
  .dash-card .meter { height: clamp(6px, calc(0.85 * var(--vh)), 10px); margin-top: calc(0.5 * var(--vh)); flex-shrink: 0; }
  .dash-card .meter-lg { height: clamp(9px, calc(1.3 * var(--vh)), 16px); margin-top: calc(0.3 * var(--vh)); flex-shrink: 0; }
  .dash-card .meter-legend { margin-top: calc(0.4 * var(--vh)); font-size: var(--fs-1); }

  /* §2 is the only card with no flex-growing chart/placeholder
     ([data-chart]/.placeholder above), so nothing else claims the stretched
     row's leftover height - margin-top: auto pushes .note (its only user) to
     the card's bottom edge instead of leaving that space dead below it. */
  .dash-card .note.note-top { margin-top: 0; }
  .dash-card .note {
    margin-top: auto;
    padding: calc(0.5 * var(--vh)) calc(0.7 * var(--vh));
    font-size: var(--fs-1);
    line-height: 1.35;
  }
  .dash-card .placeholder {
    flex: 1 1 auto;
    align-content: center;
    min-height: 0;
    margin-top: calc(0.8 * var(--vh));
    padding: calc(0.8 * var(--vh));
    font-size: var(--fs-1);
  }
  .dash-card .placeholder strong { font-size: var(--fs-1); }

  .dash-card .legend { gap: calc(0.3 * var(--vh)); font-size: var(--fs-1); }
  .dash-card .legend-row { margin-top: calc(0.2 * var(--vh)); gap: calc(0.2 * var(--vh)) 12px; }
  .dash-card .swatch { width: 9px; height: 9px; flex: 0 0 9px; border-radius: 2px; }

  .dash-card .pair { margin-top: calc(0.4 * var(--vh)); gap: calc(0.6 * var(--vh)); }
  .dash-card .pair strong { font-size: var(--fs-2); }
  /* The annotation lines beside a figure ("3.900 booking · Juni: 24,5%",
     "Bulan lalu: 27,2%") inherit the card's body size, which left them reading
     larger than the tile LABEL they sit under. Footnote size, like every other
     annotation on a card. */
  .dash-card .tile .muted,
  .dash-card .pair .muted { font-size: var(--fs-1); }
  .dash-card .pair .muted { margin-left: 6px; }

  /* §5/§8 render this instead of an axis-only chart when the division has not
     keyed the month. Centred: it stands in for a chart, so it should read as a
     state of the card, not as a stray line of text pinned to one corner. */
  .dash-card .placeholder { justify-items: center; text-align: center; }

  /* Eleven outlet names wrap to three or four rows in a quarter-width card.
     They stay at the bottom of the card and clip there rather than pushing the
     chart above them out of the cell. */
  .dash-card .chips { flex: 0 1 auto; min-height: 0; overflow: hidden; gap: 4px; margin-top: calc(0.5 * var(--vh)); }
  .dash-card .chip { padding: 2px 7px; font-size: var(--fs-1); }

  /* Short screens (anything below a full 1080p window): the 12px type floor
     and every foot line do not both fit, and a clipped foot overlapping the
     next tile is worse than no foot. Card faces keep label + value; the feet
     (vs last month, "sisa", pace notes) are all in each card's modal. */
  @media (max-height: 1000px) {
    /* .tiles .tile: outweighs §1/§2's rail rule, which sets display:block. */
    .dash .grid > .dash-card .tiles .tile .tile-foot,
    .dash .grid > .dash-card .chart-cap:not([data-section="1"] *) { display: none; }
  }
}
/* The scrolling layout runs at --z: 1, so this size is also the painted size —
   15px, over the 12px readability floor. On the HOST for the same reason as the
   one-screen rule above: charts.js reads it back and hands it to every axis, so
   the gutter and the paint agree. `text` inherits rather than declaring, so
   there is exactly one number. */
[data-chart] { font-size: 15px; color: var(--muted); }
/* `fill` is deliberately NOT set here. The renderer emits a mark's own fill as
   a PRESENTATION ATTRIBUTE, and any stylesheet rule outranks one — so a blanket
   `fill: var(--muted)` silently repainted every label that had asked for a
   colour. §6's in-band count was white-on-rust by intent and painted muted
   brown on rust instead (~1.5:1, unreadable), and §9's highlighted rows lost
   their dark ink and rendered identically to the receded ones.
   Marks that want the default emit `fill="currentColor"` and pick up the
   `color` above; marks that name a colour now keep it. */
[data-chart] text { font-size: inherit; }

/* Swiss chart restraint (2026-09-10): axis tick marks removed — the labels
   float free of the plot. Gridlines become a flat, predictable hairline
   instead of a currentColor wash. The renderer already draws no axis domain
   line (`line: false` on every axis in charts.js). */
[data-chart] .ts-chart__axes line { display: none; }
/* stroke-opacity is a separate SVG attribute from opacity — the renderer sets
   it inline to 0.11 by default, which `opacity: 1` above never touched, so
   the "hairline" read as invisible instead of subtle. */
[data-chart] .ts-chart__grid line { stroke: #e6e6e6; opacity: 1; stroke-opacity: 1; }

/* The chart library's native tooltip surface, themed to match the app instead
   of its browser-default look. These custom properties are read by the
   @tanstack/charts tooltip element's own inline styles, so restating the
   variables here is the supported override path — no internals touched. */
.ts-chart-tooltip {
  --ts-chart-tooltip-background: #fff;
  --ts-chart-tooltip-border: 1px solid var(--line);
  --ts-chart-tooltip-border-radius: 8px;
  --ts-chart-tooltip-shadow: 0 10px 28px rgb(36 26 21 / 0.14);
  /* 15px, matching the chart type it sits next to. 12px was the smallest text
     on the page and `zoom: 0.8` painted it at 9,6px — a figure someone squints
     at is a figure they read wrong. */
  --ts-chart-tooltip-font: 500 15px/1.45 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  /* The default is `min(24rem, 80%)` and the 80% is the whole bug: the tooltip
     is absolutely positioned INSIDE its chart host, so the percentage resolves
     against the host, not the page. §6's host is `.mix`'s fixed 64px chart
     track (a ~119px fraction back when this was written), which capped the
     box at ~51px; with the library's inline
     `overflow-wrap: anywhere` on top, "Classic Haircut" did not wrap, it
     SHATTERED — one letter per line, a 277px column of characters.
     Re-based on the viewport so a narrow host cannot starve it. 280px still
     clears the narrowest card this appears in, so a wide tooltip is never
     clipped by the card's own `overflow: hidden`. */
  --ts-chart-tooltip-max-width: min(280px, 92vw);
}

/* `width` is NOT set inline by the library, so this one lands normally and lets
   the box hug its content up to the cap above. */
.ts-chart-tooltip { width: max-content; }

/* These two ARE inline, and inline beats a stylesheet — `!important` is the
   only way to reach them, and it is why the max-width above goes through the
   custom property instead. Without it the row's grid gave the label a 0px
   track and printed it underneath the figure. */
.ts-chart-tooltip {
  overflow-wrap: normal !important;
  word-break: normal !important;
}
/* A row is a grid of swatch | label | figure, and the library sets the template
   inline as `0.55rem minmax(0px, 1fr) auto`. That `0px` minimum is the second
   half of the bug: the label track is allowed to shrink below its own text, so
   "Realisasi kumulatif" overflowed its 80px track and printed underneath the
   rupiah figure beside it.
   `min-content` as the floor means the track is never narrower than the label's
   longest WORD — so the label wraps onto a second line when the box runs out of
   room, which is the one behaviour that neither overlaps nor shatters. */
.ts-chart-tooltip__row {
  grid-template-columns: 0.55rem minmax(min-content, 1fr) auto !important;
}
.ts-chart-tooltip__title { white-space: nowrap; }

/* Pendapatan per Layanan (/dashboard/revenue-per-layanan) - a plain ranked
   data table, not table.pnl's freeze-pane machinery: one screen, no sticky
   caption, no per-outlet column pairs. Shares .table-scroll for the long tail
   of products; the header row freezes inside it the same way. */
table.grid {
  border-collapse: separate;
  border-spacing: 0;
  width: 100%;
  font-size: 14px;
  font-variant-numeric: tabular-nums;
}
table.grid th, table.grid td {
  padding: 6px 12px;
  border-bottom: 1px solid var(--line-soft);
  text-align: left;
  white-space: nowrap;
}
table.grid th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--bg);
  font-size: 12px;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  border-bottom: 1px solid var(--line);
}
table.grid td.num, table.grid th.num { text-align: right; }
table.grid tfoot td {
  border-top: 1px solid var(--line);
  border-bottom: 0;
  font-weight: 650;
}

/* =====================================================================   Section detail modal — opens when a section card is clicked.

   A native <dialog> in the top layer: background inert, UA-centred, height
   capped to the viewport with the body scrolling inside it.
   Cards are clickable on every viewport ≥ 1000px; below that the one-screen
   contract already shows the page as a scrolling grid, and a tap target that
   opens a modal on top would conflict with the natural scroll. The card
   itself signals its affordance via .dash-card.is-clickable + a subtle hover.
   ============================================================================ */
.dash-card.is-clickable { cursor: pointer; }
/* The hover reads as "this opens something", not as a link to somewhere else:
   the card borders onto brand and gains a hint of lift, and a chevron appears
   on the title row. A header only, so nothing on the card jumps, and the
   chevron is added by CSS (::after) so no markup change is needed. */
.dash-card.is-clickable:not(.dash-inactive):hover {
  border-color: var(--brand);
  box-shadow: 0 14px 30px -20px rgba(36, 26, 21, 0.35);
}
/* One subtle cue that the detail lives behind the card: a tiny chevron that
   appears at the end of the title row on hover. Placed on the h2 (the flex
   container), not on the title span, so a title that runs to its column's edge
   cannot clip it; kept out of the layout until hover so the header never
   shifts. */
.dash-card.is-clickable:not(.dash-inactive):hover h2::after {
  content: "\203A";
  margin-left: auto;
  color: var(--brand);
  font-size: 1.15em;
  line-height: 1;
  flex: none;
}
.dash-card.is-clickable:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.dash-inactive.is-clickable { cursor: not-allowed; }

/* Native <dialog>: showModal() puts it in the top layer (centred by the UA,
   above everything, background inert, Esc + focus trap for free). The box is
   height-capped and its body scrolls, so a long section table is reachable
   instead of painted off the bottom of a fixed overlay. */
dialog#section-modal {
  padding: 0;
  border: 0;
  border-radius: var(--radius);
  background: var(--field);
  color: var(--ink);
  width: min(1100px, calc(100vw - 48px));
  max-height: calc(100vh - 48px);
  box-shadow: 0 24px 48px -12px rgba(36, 26, 21, 0.4);
}
/* Every section-detail modal (§1's own grid + §2–§10's `body.dataset.grid`
   layout) gets the same frame. `zoom: calc(1 / var(--z))` cancels the app's
   own `body { zoom: var(--z) }` (0.8 on desktop, 1 on touch — see line ~35) so
   the modal always paints at true 1:1 scale — legible, not the 0.8 squint — and
   its `100vw`/`100vh` sizing lands in real pixels. Consistent near-full size
   with a margin around it, not edge-to-edge. Per-section layout tuning is in
   the @media block below. */
dialog#section-modal:has(.modal-body.sec-1),
dialog#section-modal:has(.modal-body[data-grid]) {
  zoom: calc(1 / var(--z));
  width: min(1440px, calc(100vw - 160px));
  /* explicit height, not just max-height: several modals stretch a chart to
     `height: 100%` of its grid cell, which only resolves against a definite
     container height. It also keeps every modal the same size. */
  height: calc(100vh - 44px);
  max-height: calc(100vh - 44px);
}
dialog#section-modal[open] {
  display: grid;
  grid-template-rows: auto 1fr;
}
dialog#section-modal::backdrop {
  background: rgba(20, 20, 20, 0.55);
}
.modal-head {
  display: flex; align-items: center; gap: 12px;
  padding: 18px 24px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--bg);
}
.modal-head h2 {
  margin: 0; flex: 1; min-width: 0;
  font-size: 20px; font-weight: 700;
  display: flex; align-items: center; gap: 10px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.modal-head h2 .num {
  display: grid; place-items: center;
  width: 28px; height: 28px; flex: 0 0 28px;
  background: var(--brand); color: var(--field);
  border: 0; border-radius: 0;
  font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums;
}
.modal-close {
  appearance: none; background: none; border: 0; padding: 0;
  width: 32px; height: 32px; min-height: 0; display: grid; place-items: center;
  border-radius: 0; cursor: pointer; color: var(--muted);
  font-size: 22px; line-height: 1;
}
.modal-close:hover { background: var(--bg); color: var(--ink); }
.modal-close:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }

.modal-body {
  /* The body scrolls inside the height-capped dialog. The head above it stays
     put; a long table reaches its last row. */
  padding: 24px;
  display: grid; gap: 20px;
  grid-template-columns: minmax(0, 1fr);
  align-content: start;
  overflow-y: auto;
  min-height: 0;
}

/* Chart heights inside the modal: deliberately smaller than the first pass
   so the chart + the table that follows it both fit on one laptop screen at
   1080p. Each value picked by the section that owns the chart - taller ones
   (leaderboards, the P&L cost matrix) earn their height, compact ones
   (layanan, sparklines) keep their natural aspect. */
/* --chart-fill:1 makes boxOf() read the fixed height below via clientHeight.
   font-size is the chart's tick/label size (readTextPx) - pinned so modal
   charts keep the projector-legible floor the .dash-card rule gives on-card. */
.modal-body [data-chart] { min-height: 280px; --chart-fill: 1; height: 320px; font-size: 15px; }
.modal-body [data-chart].spark { height: 100px; min-height: 100px; }
.modal-body [data-chart='revenue'] { min-height: 240px; height: 260px; }
.modal-body [data-chart='ytd'] { min-height: 280px; height: 320px; }
.modal-body [data-chart='outlet'] { min-height: 240px; height: 280px; }
.modal-body [data-chart='leaderboardOutlet'] { min-height: 360px; height: 400px; }
.modal-body [data-chart='leaderboardBarbertologist'] { min-height: 320px; height: 360px; }
.modal-body [data-chart='layanan'] { min-height: 160px; height: 200px; }
.modal-body [data-chart='headcount'] { min-height: 180px; height: 220px; }
.modal-body [data-chart='rate'] { min-height: 240px; height: 280px; }
.modal-body [data-chart='coa'] { min-height: 240px; height: 280px; }
.modal-body [data-chart='netProfitTrend'] { min-height: 140px; height: 180px; }
.modal-body [data-chart='pelanggan'] { min-height: 240px; height: 280px; }

.modal-section {
  display: grid; gap: 10px;
  grid-template-columns: minmax(0, 1fr); /* content stretches to the modal width, not to max-content */
}
.modal-section h3 {
  margin: 0; font-size: 12px; font-weight: 650;
  text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted);
}
.modal-tiles {
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.modal-tile {
  background: var(--bg); border: 1px solid var(--line-soft); border-radius: 8px;
  padding: 12px 14px;
  display: grid; gap: 3px;
}
.modal-tile-label { font-size: 12px; color: var(--muted); font-weight: 600; letter-spacing: 0.02em; }
.modal-tile-value { font-size: 20px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.2; }
.modal-tile-foot { font-size: 12px; color: var(--muted); }
.modal-empty { margin: 0; color: var(--muted); font-size: 13px; }
.modal-chart-legend {
  display: flex; flex-wrap: wrap; gap: 4px 14px;
  margin: 6px 0 2px; font-size: 12px; color: var(--ink);
}
.modal-chart-legend span { display: inline-flex; align-items: center; gap: 6px; }
.modal-chart-legend i {
  width: 10px; height: 10px; border-radius: 2px; flex: none;
}

/* §1 detail-modal hero (monthly + weekly) — a flat neutral panel, thin hairline
   border: the big figure, the comparison stats, and a category-filled pace rail
   with last period's position ticked. */
.s1-hero {
  border: 1px solid var(--line);
  border-radius: 0;
  background: var(--bg);
  padding: 12px 16px;
  display: grid;
  gap: 10px 26px;
}
@media (min-width: 700px) {
  .s1-hero { grid-template-columns: minmax(0, max-content) 1fr; align-items: center; }
  .s1-hero .s1-pace, .s1-hero .s1-hero-note, .s1-hero .s1-hero-wide { grid-column: 1 / -1; }
}
.s1-hero-main { display: grid; gap: 3px; align-content: start; }
.s1-hero-eyebrow {
  font-size: 12px; font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--brand);
}
.s1-hero-num {
  font-size: 30px; font-weight: 800; line-height: 1.05;
  font-variant-numeric: tabular-nums; letter-spacing: -0.01em; color: var(--ink);
}
.s1-hero-sub {
  font-size: 12px; font-weight: 600; color: var(--muted);
  font-variant-numeric: tabular-nums;
}
/* Foot under the main headline figure (§7 Swiss hero). Keeps the previous-
   period context with the number it contextualises, not as a separate cell. */
.s1-hero-foot {
  font-size: 12px; color: var(--muted);
  font-variant-numeric: tabular-nums; margin-top: 4px;
}
.s1-hero-foot.is-up { color: #1e6b33; }
.s1-hero-foot.is-up::before { content: "▲"; font-size: 9px; margin-right: 5px; }
.s1-hero-foot.is-down::before { content: "▼"; font-size: 9px; margin-right: 5px; color: var(--muted); }
.s1-hero-stats {
  display: grid; gap: 12px 20px;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
}
.s1-stat { display: grid; gap: 2px; align-content: start; }
.s1-stat-label {
  font-size: 12px; font-weight: 700; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--muted);
}
.s1-stat-value {
  font-size: 18px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ink);
}
.s1-stat-value.is-up { color: #1e6b33; } /* green up / no red down — see .delta */
.s1-stat-foot { font-size: 12px; color: var(--muted); }
.s1-pace { display: grid; gap: 6px; padding-top: 10px; }
.s1-pace-track {
  position: relative; height: 26px; border-radius: 7px; background: var(--line-soft);
}
/* the target-pace end of the track */
.s1-pace-track::after {
  content: ""; position: absolute; right: 0; top: -4px; bottom: -4px;
  width: 2px; border-radius: 1px; background: var(--brand);
}
.s1-pace-fill {
  display: flex; height: 100%; border-radius: 7px 2px 2px 7px; overflow: hidden;
  box-shadow: inset 0 0 0 1px rgba(36, 26, 21, 0.06);
  background: var(--brand); /* a segmentless rail (§2) still shows a fill */
}
/* a bridge / composition rail (§4 margin, §6 job-mix, §7 base) is the whole
   100%, so the target-pace end tick has nothing to mark */
.s1-pace-track.no-end::after { content: none; }
/* each category segment centres its juta value; a segment with no room for one
   just renders as a colour band (charts.js only appends the span where it fits) */
.s1-pace-fill i {
  display: flex; align-items: center; justify-content: center;
  height: 100%; min-width: 0; overflow: hidden;
}
.s1-pace-fill i + i { box-shadow: inset 1px 0 0 rgba(246, 236, 228, 0.4); }
.s1-pace-fill i span {
  font-size: 12px; font-weight: 650; font-style: normal; color: #fff;
  font-variant-numeric: tabular-nums; letter-spacing: 0.01em;
  padding: 0 4px; white-space: nowrap;
}
.s1-pace-ghost { position: absolute; top: -4px; bottom: -4px; width: 2px; background: var(--ink); }
.s1-pace-ghost::before {
  content: ""; position: absolute; left: 50%; top: -4px;
  width: 6px; height: 6px; background: var(--ink);
  transform: translateX(-50%) rotate(45deg);
}
.s1-pace-ghost::after {
  content: attr(data-month);
  position: absolute; left: 50%; top: -17px; transform: translateX(-50%);
  font-size: 12px; font-weight: 700; letter-spacing: 0.02em; color: var(--ink); white-space: nowrap;
}
.s1-pace-labels {
  display: flex; justify-content: space-between;
  font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums;
}
.s1-hero-note { margin: 0; font-size: 12px; color: var(--muted); }
.k-dot {
  display: inline-block; width: 9px; height: 9px; border-radius: 2px;
  margin-right: 7px; vertical-align: baseline;
}

/* §1 detail modal — outlet contribution as a compact ranked list (both the
   monthly and weekly views use it). An 11-row bordered table alone stood taller
   than the whole modal. Row: rank · name · dotted leader · juta · share — the
   leader ties the name to its figure across the gap and the figures line up on
   the right edge of each column. */
.s1-contrib { display: grid; gap: 3px; }
.s1-contrib-row {
  display: grid;
  grid-template-columns: 1.4em max-content minmax(12px, 1fr) 4.6em 3em;
  align-items: baseline; column-gap: 6px;
  padding: 2.5px 0;
  font-size: 12px; font-variant-numeric: tabular-nums;
}
.s1-contrib-rank { color: var(--muted); font-size: 12px; text-align: right; }
.s1-contrib-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink); }
.s1-contrib-lead { align-self: center; height: 0; border-bottom: 1px dotted var(--line); }
.s1-contrib-jt { text-align: right; white-space: nowrap; color: var(--ink); font-weight: 600; }
.s1-contrib-pct { text-align: right; white-space: nowrap; color: var(--muted); }

/* §1's Komposisi Layanan — a sorted horizontal bar list (charts.js
   katCompositionBars). The .k-bars grid owns the columns; each row is a subgrid
   so every track shares one width (bars only compare if the baseline does) and
   the figures line up on the right. The 1fr track eats the column's free width,
   so the block fills the modal cell the snug table used to float in. */
.k-bars {
  display: grid;
  grid-template-columns: max-content minmax(36px, 1fr) 4em 4.6em;
  gap: 5px 10px;
  font-size: 12px; font-variant-numeric: tabular-nums;
}
/* Two independent .k-bars side by side (§6's per-outlet list once the roster
   is long) - each keeps its own subgrid, so tracks only line up within a
   column, not across both. */
.k-bars-cols {
  display: grid;
  grid-template-columns: repeat(var(--k-cols, 2), 1fr);
  gap: 5px 14px;
}
.k-bars:has(.k-bar-shift) {
  grid-template-columns: max-content minmax(36px, 1fr) 4em 4.6em 3.9em;
}
/* Target-per-produk scorecard (charts.js katTargetList) — §1 modal AND card
   face. Short product names, wider "−528,3"-style gaps than Tren Bulanan's
   "+12,4%": its own column widths, plus left room so the name clears the
   is-bad / is-good inset edge. */
/* .k-bars.k-target, not .k-target — matches .k-bars:has(.k-bar-shift)'s
   specificity so the later rule wins the column override. */
.k-bars.k-target { grid-template-columns: max-content minmax(20px, 1fr) 4.2em 5.4em 6em; }
/* Compare mode (?bandingkan=): a "Δ Banding" column. Modal keeps Aktual +
   Selisih and adds it (5 cols); the card face swaps Selisih out for it (4).
   %Target dropped as its own column (2026-09-16) — the bar's own green/red
   split already carries that number; the freed width goes to the bar. */
.k-bars.k-target.has-band { grid-template-columns: max-content minmax(20px, 1fr) 4.2em 5.4em 6em 6em; }
/* Card-face compare mode: name · bar · one big Δ column, nothing else. */
.k-bars.k-target.has-band.band-compact { grid-template-columns: max-content minmax(20px, 1fr) 6.5em; }
.k-bar-band { text-align: right; color: var(--muted); font-size: 12px; white-space: nowrap; }
.k-bar-band.is-up { color: #1e6b33; font-weight: 600; }
.k-bar-band.is-down { color: #a3301a; font-weight: 600; }
.band-compact .k-bar-row:not(.k-bar-head) .k-bar-band { font-size: 15px; font-weight: 700; }
.k-target .k-bar-name { padding-left: 4px; }
/* Hidden by default; the one-screen monthly layout switches it on for §1
   (see the dashboard @media block) where it stands in for the revenue chart. */
[data-s1-target] { display: none; }
/* Same deal for the §1 progress-bar caption: the one-screen face shows it in
   place of the "% dari Target" tile; the narrow layout keeps that tile and
   leaves this hidden, so the figure is never on screen twice. */
.s1-progress-cap { display: none; }

/* §10 card face — two-column ranked list (charts.js [data-barber-strip] loop),
   the one-screen stand-in for the 20-row Haircut/Package chart, which cannot
   fit this cell legibly. Hidden by default; the one-screen @media block
   switches it on for §10 and hides the chart + legend (kept in the DOM for the
   modal). Haircut only, no bars; top column green, bottom red. */
[data-barber-strip] { display: none; }
/* Fills the §10 cell top-to-bottom: the strip stretches (flex:1 on the one-screen
   rule), each column is a full-height grid, and align-content:space-between
   spreads the 10 rows down the card so there is no dead space below rank 10 —
   the row PITCH grows, the bars stay one text-line tall. */
.bstrip {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: 0 clamp(14px, 2.4vw, 34px); height: 100%; min-height: 0;
}
.bstrip-col {
  display: grid; grid-template-columns: 2ch 1fr max-content;
  column-gap: 8px; align-content: space-between;
}
.bstrip-head {
  grid-column: 1 / -1; margin: 0; text-align: center;
  font-size: var(--fs-1, 12px);
  letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--muted); font-weight: 650;
}
.bstrip-row {
  display: grid; grid-column: 1 / -1; grid-template-columns: subgrid;
  align-items: center; line-height: 1.15;
  font-size: var(--fs-1, 13px);
  font-variant-numeric: tabular-nums;
}
.bstrip-rank { color: var(--muted); text-align: right; }
.bstrip-name {
  color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.bstrip-num { text-align: right; font-weight: 700; }
/* Top 10 green, bottom 10 red (2026-09-29, on request) — name and figure, the
   same pair §9's outlet leaderboard colours. */
.bstrip-col.is-top :is(.bstrip-name, .bstrip-num) { color: #1e6b33; }
.bstrip-col.is-bottom :is(.bstrip-name, .bstrip-num) { color: #a3301a; }
.k-bar-row {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
  align-items: center;
}
.k-bar-head {
  font-size: 12px; letter-spacing: 0.03em; text-transform: uppercase;
  color: var(--muted); font-weight: 650;
}
.k-bar-head span { text-align: right; white-space: nowrap; }
.k-bar-name { color: var(--ink); white-space: nowrap; }
.k-bar-track {
  height: 12px; border-radius: 3px; overflow: hidden;
  background: var(--line-soft);
}
.k-bar-track i { display: block; height: 100%; min-width: 2px; border-radius: 3px; }
.k-bar-jt { text-align: right; font-weight: 600; color: var(--ink); white-space: nowrap; }
.k-bar-pct { text-align: right; color: var(--muted); white-space: nowrap; }
/* Komposisi: mix-shift vs last month — directional, not good/bad (charts.js
   ppShift). Tren: MoM. Both right-aligned in the last column. */
.k-bar-shift { text-align: right; color: var(--muted); font-size: 12px; }

/* Tren Bulanan: the bar is % of that month's own target (0–100, tick at 100),
   in brand rather than the per-service colours. */
.t-bar-track { position: relative; }
.t-bar-track i { background: var(--brand); }
.t-bar-track::after {
  content: ""; position: absolute; right: 0; top: 0; bottom: 0;
  width: 2px; background: var(--ink); opacity: 0.35;
}

/* §1 card-face compare mode: diverging Δ bar (charts.js katTargetList,
   compactBand). Centre line at 50%; fill grows right for a revenue gain vs the
   comparison period, left for a loss. Length ∝ this row's |Δ| / the biggest. */
.k-div-track {
  position: relative; height: 12px; border-radius: 3px;
  background: var(--line-soft);
}
.k-div-track::before {
  content: ""; position: absolute; left: 50%; top: -1px; bottom: -1px;
  width: 1px; background: var(--ink); opacity: 0.3;
}
.k-div-fill {
  display: block; position: absolute; top: 0; bottom: 0; min-width: 2px;
}
.k-div-fill.pos { left: 50%; background: #1e6b33; border-radius: 0 3px 3px 0; }
.k-div-fill.neg { right: 50%; background: #a3301a; border-radius: 3px 0 0 3px; }

/* §8's headcount bridge as its own widget (charts.js waterfallBlock): step
   label · the step's own ± · the running total. */
.m-wf {
  display: grid; gap: 2px;
  font-size: 12px; font-variant-numeric: tabular-nums;
}
.m-wf-row {
  display: grid; grid-template-columns: 1fr max-content 4.5em;
  align-items: baseline; column-gap: 12px; padding: 3px 6px;
}
.m-wf-label { color: var(--ink); }
.m-wf-delta { text-align: right; color: var(--muted); }
.m-wf-run { text-align: right; font-weight: 700; color: var(--ink); }

/* Modal highlight rows — doctrine in charts.js (applyMark) and
   docs/dashboard-highlight-rules.md. This page carries no red (see .delta):
   "watch" is a warm ochre, "good" the same green .delta.up uses. A row tint
   plus a 2px inset edge, so the mark survives the row gaps in .k-bars /
   .s1-contrib and still reads if the tint is missed. */
.k-bar-row.is-watch, .s1-contrib-row.is-watch, .modal-table tr.is-watch, .m-wf-row.is-watch {
  background: #f7ece0; box-shadow: inset 2px 0 0 #a75d17;
}
.k-bar-row.is-good, .s1-contrib-row.is-good, .modal-table tr.is-good, .m-wf-row.is-good {
  background: #e8f1ea; box-shadow: inset 2px 0 0 #1e6b33;
}
/* `is-bad` = a pass/fail miss (§1's per-product target scorecard). The one red
   on the page — the app's own negative colour (.tile-value.neg, .flash.err),
   reserved for "this number did not hit its target", never for a nudge. */
.k-bar-row.is-bad, .s1-contrib-row.is-bad, .modal-table tr.is-bad, .m-wf-row.is-bad {
  background: #fbeeed; box-shadow: inset 2px 0 0 #a3301a;
}
.k-bar-row.is-bad .k-bar-pct, .k-bar-row.is-bad .k-bar-shift { color: #a3301a; font-weight: 600; }
.k-bar-row.is-good .k-bar-pct, .k-bar-row.is-good .k-bar-shift { color: #1e6b33; font-weight: 600; }
.k-bar-row.is-bad .k-bar-track i:not(.k-bar-fill-good):not(.k-bar-fill-bad) { background: #a3301a; }
.k-bar-row.is-good .k-bar-track i:not(.k-bar-fill-good):not(.k-bar-fill-bad) { background: #1e6b33; }

/* §1 "Target per produk": % Tercapai in its own column, dark tabular text. */
.k-target .k-bar-pct { font-variant-numeric: tabular-nums; }
.k-bar-row.is-watch, .k-bar-row.is-good, .k-bar-row.is-bad { padding-block: 2px; }
.k-bar-row.is-watch .k-bar-pct, .k-bar-row.is-watch .k-bar-shift,
.s1-contrib-row.is-watch .s1-contrib-pct { color: #8a4c11; font-weight: 600; }
.s1-contrib-row.is-good .s1-contrib-pct { color: #1e6b33; font-weight: 600; }
.modal-table tr.is-watch td:last-child { color: #8a4c11; font-weight: 600; }
.modal-table tr.is-good td:last-child { color: #1e6b33; font-weight: 600; }
.modal-table tr.is-bad td:last-child { color: #a3301a; font-weight: 600; }
.modal-table td.is-pos { color: #1e6b33; font-weight: 600; }
.modal-table td.is-neg { color: #a3301a; font-weight: 600; }
/* §6 modal's Komposisi per Item per Outlet, weekly mode + ?bandingkan= only:
   heatmap — current figure on top, Δ% as a small grey line, the cell tint
   carries direction + size (charts.js heatOf: any change tinted, 3 steps;
   both periods < 20 fades). */
.modal-table .cell-cmp { display: flex; flex-direction: column; align-items: flex-end; }
.modal-table .cell-cmp-was { font-size: 12px; line-height: 1.3; font-variant-numeric: tabular-nums; color: #6b6b6b; }
.modal-table td.heat-pos-1 { background: #eef5ef; }
.modal-table td.heat-pos-2 { background: #d6e9da; }
.modal-table td.heat-pos-3 { background: #b5d8bd; }
.modal-table td.heat-neg-1 { background: #fbefec; }
.modal-table td.heat-neg-2 { background: #f5d7d0; }
.modal-table td.heat-neg-3 { background: #ecb6aa; }
.modal-table td[class*="heat-pos"] .cell-cmp-was { color: #1e6b33; font-weight: 600; }
.modal-table td[class*="heat-neg"] .cell-cmp-was { color: #a3301a; font-weight: 600; }
.modal-table td.is-lowbase .cell-cmp { opacity: .55; }
/* sortable-table Total: a summary, set apart softly — a faint grey band, a
   hairline above and semibold figures (a 2px ink rule was too harsh,
   2026-09-27). No heat fill on its cells: the total is the benchmark the
   outlet cells are read against; its Δ% keeps its color. §6 Komposisi per
   Item's Total column gets a matching hairline on its left edge. */
.modal-table tr.is-done td { background: #f6ece7; }  /* closed months in the Proyeksi table */
.modal-table tr.is-total td { font-weight: 600; border-top: 1px solid #a8a8a8; background: #f5f5f5; }
.modal-table tr.is-total td[class*="heat-"] { background: #f5f5f5; }
.modal-body.sec-6 .ga-e .modal-table :is(th, td):last-child { border-left: 1px solid #bdbdbd; font-weight: 600; }
/* sortable-table extremes: row with the sorted column's highest / lowest value */
.modal-table tr.is-hi { background: #e8f1ea; box-shadow: inset 2px 0 0 #1e6b33; }
.modal-table tr.is-lo { background: #fbeeed; box-shadow: inset 2px 0 0 #a3301a; }
/* click-to-pick: keep one row tinted brown so a name stays findable while
   scrolling. Wins over the hi/lo tint on the same row (declared after). */
.modal-table.is-pickable tbody tr { cursor: pointer; }
.modal-table.is-pickable tbody tr:hover { background: var(--bg); }
.modal-table tr.is-picked,
.modal-table.is-pickable tbody tr.is-picked:hover {
  background: #ecdcc4; box-shadow: inset 3px 0 0 #8a5a2b;
}
.modal-hl-cap {
  margin: 5px 0 0; font-size: 12px; color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* The modal's opening finding (charts.js verdict()). Reads before the tiles —
   sized like body copy, not a caption, with the same left rule the row marks
   use. `flat` = neutral (a section with no drama says so plainly). */
.modal-verdict {
  padding: 11px 15px; border-radius: 8px;
  background: var(--bg); box-shadow: inset 3px 0 0 var(--muted);
  font-size: 14px; line-height: 1.55; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.modal-verdict.is-watch { background: #f7ece0; box-shadow: inset 3px 0 0 #a75d17; }
.modal-verdict.is-good  { background: #e8f1ea; box-shadow: inset 3px 0 0 #1e6b33; }
.modal-verdict ul { margin: 0; padding-left: 17px; display: grid; gap: 4px; }

/* Tables inside the modal grow with their rows; the modal body scrolls around
   them. The bordered wrap gives the table its own card-like surface so it reads
   as a unit rather than floating in the modal's white space. */
/* Modal tables are their own thing - NOT `.grid` (that class is `display:grid`
   for the card layout and turns a <table> into a broken grid). Just a scroll
   container for the odd table wider than the modal; the rows carry their own
   rules so no enclosing box is needed. */
.modal-table-wrap { overflow-x: auto; }
.modal-table {
  /* content width, left-aligned - a snug table reads better than one justified
     across the whole modal with the columns floating apart. Boxed by an outer
     border; no inner vertical rules, rows carry the horizontal lines. */
  width: auto;
  max-width: 100%;
  border-collapse: collapse;
  border: 1px solid var(--line);   /* outer box only — no inner vertical rules */
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.modal-table td.num, .modal-table th.num { padding-left: 28px; } /* breathing room between columns */
.modal-table th, .modal-table td {
  padding: 7px 12px;
  border-bottom: 1px solid var(--line); /* was --line-soft, too faint to follow a row (2026-09-27) */
  text-align: left;
  white-space: nowrap;
}
/* Colored header band: filled brand row, white uppercase labels. */
.modal-table th {
  font-size: 12px; font-weight: 650; color: #fff;
  text-transform: uppercase; letter-spacing: 0.03em;
  background: var(--brand);
  border-bottom: 0;
  padding: 8px 12px;
}
.modal-table td.num, .modal-table th.num { text-align: right; }
/* Column rules (2026-09-27, on request): same weight as the row lines; on the
   brown header band a translucent white so it doesn't cut the band. */
.modal-table td + td { border-left: 1px solid var(--line); }
.modal-table th + th { border-left: 1px solid rgb(255 255 255 / 0.25); }
/* Two-row header (sortableTableBlock col.group): group label centred over its
   columns; the second row's first cell sits beside a rowspan cell, so it needs
   the column rule `th + th` can't give it. */
.modal-table th.th-group { text-align: center; border-bottom: 1px solid rgb(255 255 255 / 0.25); }
.modal-table thead tr + tr th:first-child { border-left: 1px solid rgb(255 255 255 / 0.25); }
.modal-table tbody tr:last-child td { border-bottom: 0; }

/* §10's sortable headers only (charts.js sortableTableBlock, 2026-09-07) -
   every other modal table's th stays static, so this keys on the marker
   attribute the builder sets rather than a blanket .modal-table th rule. */
.modal-table th[data-sortable] {
  cursor: pointer;
  user-select: none;
}
.modal-table th[data-sortable]:hover { background: var(--brand-dark); }
.modal-table th[data-sortable].is-sorted { background: var(--brand-dark); }
.modal-table th[data-sortable]:focus-visible {
  outline: 2px solid #fff;
  outline-offset: -3px;
}

/* §5 modal — Findings SQ Database pie beside the table that names its slices. */
.findings-pie { display: grid; grid-template-columns: minmax(0, 200px) minmax(0, 1fr) minmax(0, 1.4fr); gap: 22px; align-items: center; }
.findings-pie [data-chart] { height: 200px; min-height: 200px; }
.findings-dot { display: inline-block; width: 9px; height: 9px; margin-right: 8px; }
@media (max-width: 900px) { .findings-pie { grid-template-columns: 1fr; } }

/* §1 detail modal, MONTHLY and WEEKLY alike (one layout since 2026-09-29 —
   monthly used to squeeze its tables into a 1/3 column beside the chart):
   hero, chart, Revenue per Category, Pendapatan per Minggu and outlet
   contribution each full-width, top to bottom, at a readable size.
   Below 1000px it falls back to the single-column flow. */
@media (min-width: 1000px) {
  .modal-body.sec-1 {
    grid-template-columns: 1fr;
    grid-template-areas: "hero" "chart" "mix" "compo" "trend" "region" "outlet";
    row-gap: 8px;
  }
  /* align-content:start stops a short section's rows stretching to fill the
     equal-height grid cell. */
  .modal-body.sec-1 .modal-section { gap: 6px; align-content: start; }

  .modal-body.sec-1 .s1-hero   { grid-area: hero; align-self: start; }
  .modal-body.sec-1 .s1-chart  { grid-area: chart; padding: 0; border: 0; align-content: start; }
  .modal-body.sec-1 .s1-mix    {
    grid-area: mix; align-self: start;
    border-top: 1px solid var(--line); padding-top: 10px;
  }
  .modal-body.sec-1 .s1-compo  {
    grid-area: compo; align-self: start;
    border-top: 1px solid var(--line); padding-top: 10px;
  }
  .modal-body.sec-1 .s1-trend  { grid-area: trend; align-self: start; }
  .modal-body.sec-1 .s1-outlet {
    grid-area: outlet;
    border-top: 1px solid var(--line);
    padding-top: 10px;
  }
  .modal-body.sec-1 .s1-chart [data-chart] { height: 300px; min-height: 300px; }
  .modal-body.sec-1 .s1-chart .modal-chart-legend { margin: 5px 0 0; }
  .modal-body.sec-1 .s1-chart-ritme {
    margin: 4px 0 0; font-weight: 600; color: var(--ink);
  }
  .modal-body.sec-1 .k-bars { gap: 10px 14px; font-size: 14px; }
  .modal-body.sec-1 .k-bar-track { height: 22px; }
  .modal-body.sec-1 .k-bar-head { font-size: 12px; }
  .modal-body.sec-1 .modal-table { font-size: 14px; width: 100%; }
  .modal-body.sec-1 .s1-region { grid-area: region; align-self: start; }
  .modal-body.sec-1 :is(.s1-trend, .s1-region) .modal-table-wrap { width: 100%; }
  .modal-body.sec-1 .modal-table :is(th, td) { padding-block: 9px; line-height: 1.6; }
  .modal-body.sec-1 .modal-table th { padding-block: 11px; }
  .modal-body.sec-1 .modal-table :is(th, td).num { width: 4.2rem; }
  /* outlet: three top-to-bottom columns. */
  .modal-body.sec-1 .s1-contrib {
    display: block; columns: 3; column-gap: 36px;
  }
  .modal-body.sec-1 .s1-contrib-row {
    break-inside: avoid; font-size: 14px; padding: 4px 0; column-gap: 8px;
  }
  .modal-body.sec-1 .s1-contrib-rank { font-size: 12px; }

  /* ---- §2–§10 enriched modals: fit-one-screen grid (charts.js gArea) --------
     Every one lays out as: hero across the top, verdict under it, then the
     chart and one or two tables sharing the columns below — sized so a 1080p
     laptop never scrolls the body, same discipline as sec-1. The named areas
     (hero/verdict/chart/a/b/c) are placed per section; the shared rules here
     do the tightening. Below 1000px this whole block is off and the body
     falls back to the single-column flow. */
  .modal-body[data-grid] {
    column-gap: 22px;
    row-gap: 8px;
    align-content: start;
  }
  .modal-body[data-grid] .ga-hero    { grid-area: hero; align-self: start; }
  .modal-body[data-grid] .ga-verdict { grid-area: verdict; align-self: start; }
  .modal-body[data-grid] .ga-chart   { grid-area: chart; align-self: start; }
  .modal-body[data-grid] .ga-trend   { grid-area: trend; align-self: start; }
  .modal-body[data-grid] .ga-a       { grid-area: a; align-self: start; }
  .modal-body[data-grid] .ga-b       { grid-area: b; align-self: start; }
  .modal-body[data-grid] .ga-c       { grid-area: c; align-self: start; }
  .modal-body[data-grid] .ga-d       { grid-area: d; align-self: start; }
  .modal-body[data-grid] .ga-e       { grid-area: e; align-self: start; }
  .modal-body[data-grid] .ga-f       { grid-area: f; align-self: start; }
  .modal-body[data-grid] .ga-g       { grid-area: g; align-self: start; }
  .modal-body[data-grid] .ga-h       { grid-area: h; align-self: start; }
  .modal-body[data-grid] .ga-j       { grid-area: j; align-self: start; }
  .modal-body[data-grid] .ga-i       { grid-area: i; align-self: start; }

  .modal-body[data-grid] .modal-section { gap: 6px; align-content: start; }
  /* Komposisi YTD beside the Kumulatif table (2026-09-30): take the table's
     height and spread the bar rows down it instead of hugging the top. */
  .modal-body[data-grid] .modal-section.fill-row { align-self: stretch; grid-template-rows: auto 1fr; }
  .modal-body[data-grid] .fill-row .k-bars { align-content: space-between; }
  .modal-body[data-grid] .modal-section h3 { font-size: 12px; }
  /* tight table rows — several share a column with the chart */
  .modal-body[data-grid] .modal-table { font-size: 12px; }
  .modal-body[data-grid] .modal-table :is(th, td) { padding-block: 2px; line-height: 1.25; }
  .modal-body[data-grid] .modal-table th { padding-block: 5px; }
  .modal-body[data-grid] .modal-table :is(th, td).num { padding-left: 18px; }
  /* the modal's default 280–400px chart heights are for a full-width chart;
     here the chart shares a row with tables, so it takes what the tallest
     table beside it needs — capped low. */
  .modal-body[data-grid] .modal-chartwrap [data-chart] { height: 188px; min-height: 172px; }
  .modal-body[data-grid] .modal-chartwrap [data-chart].spark { height: 70px; min-height: 70px; }
  .modal-body[data-grid] .s1-hero { padding: 9px 13px; gap: 7px 20px; }
  .modal-body[data-grid] .s1-hero-num { font-size: 25px; }
  .modal-body[data-grid] .s1-hero-stats { gap: 8px 16px; }
  /* The pace-descriptor text under each stat ("pace ideal 58,0%", "perlu
     Rp 1,1 M/bln") carries the modal's second-most-important numbers — 11px
     fell below comfortable reading at this viewing distance. */
  .modal-body[data-grid] .s1-stat-foot { font-size: 12px; }
  /* §6 / §9 used to flatten their hero (headline same size as the stats,
     no dominant number) — superseded 2026-09-12 by the Swiss dominant-headline
     treatment (.is-swiss, below) applied to every section's hero. */
  /* Swiss-style hero, all sections (§1–9): a dominant headline band with a
     ruled row of equal-width supporting metrics, each centered independently
     (statCell's variable foot length is the reason for centering, not
     top-alignment — see §7's "Moving" label-wrap fix, 2026-09-12: give every
     stat the same number of lines, a ' ' placeholder foot where a section has
     none of its own, or centering re-misaligns the row). flex:1 1 0 on each
     .s1-stat adapts to any stat count (§9 has 3, §2 up to 5) with no
     hardcoded column template. */
  /* .modal-body prefix isn't scoping — it's specificity: needs to beat the
     (0,3,0) [data-grid] .s1-hero-num etc rules just above regardless of
     source order, since is-swiss is applied per-hero via JS, not per-section
     in CSS. */
  .modal-body .s1-hero.is-swiss {
    display: grid;
    grid-template-columns: minmax(220px, max-content) 1fr;
    gap: 0;
    align-items: stretch;
    background: transparent;
    border: 0;
    border-top: 2px solid var(--ink);
    border-bottom: 1px solid var(--line);
    padding: 0;
  }
  .modal-body .s1-hero.is-swiss .s1-hero-main {
    justify-content: start;
    gap: 4px;
    padding: 18px 26px;
    background: transparent;
    border-right: 1px solid var(--line);
  }
  .modal-body .s1-hero.is-swiss .s1-hero-eyebrow {
    font-size: 12px; letter-spacing: 0.1em;
  }
  .modal-body .s1-hero.is-swiss .s1-hero-num {
    font-size: 42px; line-height: 1;
  }
  .modal-body .s1-hero.is-swiss .s1-hero-foot {
    font-size: 13px; margin-top: 6px;
  }
  .modal-body .s1-hero.is-swiss .s1-hero-stats {
    display: flex;
    align-items: stretch;
    background: transparent;
  }
  .modal-body .s1-hero.is-swiss .s1-stat {
    flex: 1 1 0;
    justify-content: start;
    gap: 5px;
    padding: 18px 12px;
    background: transparent;
    border-left: 1px solid var(--line);
    min-width: 0;
  }
  .modal-body .s1-hero.is-swiss .s1-stat:first-child { border-left: 0; }
  /* min-height reserves 2 lines always — some labels wrap ("PACE TARGET /
     MINGGU"), others don't ("KEKURANGAN"); without this the value below sits
     one line higher wherever the label happened to fit on one line, breaking
     the row's baseline across cells even though every cell top-aligns. */
  .modal-body .s1-hero.is-swiss .s1-stat-label {
    font-size: 12px; letter-spacing: 0.08em; color: var(--muted);
    line-height: 1.3; min-height: 2.6em;
  }
  /* "Rp 282,3 Jt" etc must read as one figure, not wrap mid-unit ("Jt" alone
     on its own line) — nowrap + a size that fits the narrowest stat cell. */
  .modal-body .s1-hero.is-swiss .s1-stat-value {
    font-size: 19px; line-height: 1.15; white-space: nowrap;
  }
  .modal-body .s1-hero.is-swiss .s1-stat-foot {
    font-size: 12px; margin-top: 4px;
  }
  /* §7 "Baru bulan ini" folds the outlet-age split into this one cell instead
     of its own wide row below (on request, 2026-09-16) — total centered on
     its own line, new-outlet/old-outlet on the line under it. */
  .modal-body .s1-hero.is-swiss .s1-stat-split-total {
    display: block; text-align: center;
  }
  /* Centered as a tight group, not stretched edge-to-edge (found live,
     2026-09-16: with fewer than 5 stat cells in the row this cell gets wide
     via flex:1, and space-between pushed "Baru" and "Lama" apart from each
     other and from the total above). */
  .modal-body .s1-hero.is-swiss .s1-stat-split-row {
    display: flex; justify-content: center; gap: 2px 16px; flex-wrap: wrap;
    font-size: 12px; color: var(--muted); margin-top: 2px;
  }

  .modal-body[data-grid] .modal-verdict { padding: 8px 12px; font-size: 12.5px; line-height: 1.4; }
  .modal-body[data-grid] .modal-verdict ul { gap: 3px; }
  .modal-body[data-grid] .k-bars { gap: 3px 10px; font-size: 12px; }

  /* §2 — hero (pace rail dropped 2026-09-12), then one full-width row per
     chart (revenue vs target / kumulatif, then Haircut 2026 vs 2025
     in `c`) so neither plot is squeezed against a table (2026-09-24). The
     two non-chart blocks — Komposisi YTD bars and the Kumulatif vs Target
     table — share the last row instead, since both are read as lists/tables,
     not plots. The table column runs wider (1.5fr): it carries up to 7
     columns, the bars need only a name + a number. */
  .modal-body.sec-2 {
    grid-template-columns: 1fr 1.5fr;
    grid-template-areas:
      "hero  hero"
      "chart chart"
      "c     c"
      "d     d"
      "f     f"
      "e     e"
      "b     a";
  }
  .modal-body.sec-2 .modal-chartwrap [data-chart] { font-size: 15px; }
  .modal-body.sec-2 .ga-a { align-self: stretch; grid-template-rows: auto 1fr; }
  .modal-body.sec-2 .ga-a .modal-table-wrap { height: 100%; }
  /* The page doctrine keeps tables content-width (snug beats justified), but
     that doctrine is about a table floating across the WHOLE modal width. In
     a stretched grid cell of its own, an auto-width box stops partway across
     the cell and reads as an unfinished edge — so here the table justifies to
     its own column, the numeric columns keeping their right alignment. */
  .modal-body.sec-2 .ga-a .modal-table { height: 100%; width: 100%; }
  /* Each chart runs alone in its own full-width row now, so its height is a
     fixed lever rather than "stretch to match the sibling beside it". */
  .modal-body.sec-2 :is(.ga-chart, .ga-c, .ga-d, .ga-f) [data-chart] { height: 260px; min-height: 220px; }
  /* Proyeksi Revenue fan chart (§2 and §9's per-outlet copy, 2026-10-03). */
  .modal-body[data-grid] .modal-chartwrap :is([data-chart="proyeksi"], [data-chart="proyeksiBulan"]) { height: 380px; min-height: 280px; margin-top: 8px; }
  .modal-body .ga-e .proyeksi-switches { display: flex; flex-wrap: wrap; gap: 8px 16px; margin-top: 12px; }
  .modal-body .ga-e .proyeksi-switches .mode-toggle { width: max-content; }
  .modal-body .ga-e details { margin-top: 8px; }
  .modal-body .ga-e summary { cursor: pointer; font-size: 13px; color: var(--muted); }

  /* §3 Net Profit — hero / trend / trajectory table / rincian, one column. */
  .modal-body.sec-3 {
    grid-template-columns: 1fr;
    grid-template-areas:
      "hero"
      "trend"
      "chart"
      "rincian";
  }
  /* Cohort bars: a DASHED mark at the portfolio average on every track, so it
     reads as a reference line rather than a second bar segment — the solid
     right-edge tick is the 100% cap. Same mark, same place, every row. */
  .modal-body .o3-track { height: 14px; }
  .modal-body .o3-track::before {
    content: ""; position: absolute; top: -3px; bottom: -3px;
    left: var(--avg, 100%); width: 0;
    border-left: 2px dashed var(--ink); opacity: 0.45;
  }
  .modal-body .o3-track::after { opacity: 0.18; }
  /* The block's rows sit inside a 2-row bar list where the house row-tint
     (full-width band + inset edge) reads as a clipped table: text flush to
     the band's edge on both sides. Here the mark moves onto the name — a
     short accent edge + padding on the name cell only, so columns stay on
     their shared subgrid — and the block gets inner padding instead. */
  .modal-body.sec-6 .ga-a .k-bars { padding: 3px 8px; }
  /* Natural height, top-aligned (2026-09-27): it used to stretch to the
     chart's full height, which spread six rows ~90px apart. */
  .modal-body.sec-6 .ga-a { align-self: start; }
  .modal-body.sec-6 .ga-a .modal-table { width: 100%; }

  /* §4 Budget — divisi trend row, trajectory beside the budget table, then
     the rincian. The net-profit hero moved to §3. */
  .modal-body.sec-4 {
    grid-template-columns: 1.2fr 1fr;
    grid-template-areas:
      "trend trend"
      "chart a"
      "rincian rincian";
  }
  .modal-body[data-grid] .ga-rincian { grid-area: rincian; }
  .modal-body:is(.sec-3, .sec-4) .s4-rincian { border: 1px solid var(--line); padding: 0 12px 12px; }
  .modal-body:is(.sec-3, .sec-4) .s4-rincian-tables {
    display: grid; gap: 12px; align-items: start;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .modal-body.sec-3 .s4-rincian-tables { grid-template-columns: 1fr; }
  .modal-body:is(.sec-3, .sec-4) .s4-rincian-stack { display: grid; gap: 12px; align-content: start; }
  @media (max-width: 720px) {
    .modal-body:is(.sec-3, .sec-4) .s4-rincian-tables { grid-template-columns: 1fr; }
  }
  .modal-body:is(.sec-3, .sec-4) .s4-rincian .s4-trend-head { align-items: center; margin-bottom: 10px; }
  .modal-body:is(.sec-3, .sec-4) .s4-rincian .modal-table { width: 100%; }
  .modal-body:is(.sec-3, .sec-4) .s4-rincian tr.is-subtotal td {
    font-weight: 600; border-top: 1px solid var(--line);
  }
  .modal-body:is(.sec-3, .sec-4) .s4-month-picker {
    margin-left: 10px; padding: 2px 6px; font: inherit; font-size: 13px; font-weight: 400;
    color: var(--ink); background: var(--field, #fff); border: 1px solid var(--line);
  }
  .modal-body:is(.sec-3, .sec-4) .s4-trends { display: grid; gap: 12px; }
  .modal-body:is(.sec-3, .sec-4) .s4-trend { border: 1px solid var(--line); padding: 0 12px 6px; }
  .modal-body:is(.sec-3, .sec-4) .s4-trends .modal-chartwrap [data-chart] {
    height: 200px; min-height: 180px;
  }
  .modal-body:is(.sec-3, .sec-4) .s4-trends .s4-outlet [data-chart] { height: 340px; }
  /* Panel header: name left, where the period stands right — red when it misses. */
  .modal-body:is(.sec-3, .sec-4) .s4-trend-head {
    display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
    margin: 0 -12px 4px; padding: 7px 12px;
    background: color-mix(in srgb, var(--line-soft) 55%, var(--field));
    border-bottom: 1px solid var(--line);
  }
  .modal-body:is(.sec-3, .sec-4) .s4-trend-head h3 { margin: 0; font-size: 13px; color: var(--ink); }
  .modal-body:is(.sec-3, .sec-4) .s4-trend-head span {
    font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums;
  }
  .modal-body:is(.sec-3, .sec-4) .s4-trend-head span.is-bad { color: #a3301a; }
  .modal-body:is(.sec-3, .sec-4) .s4-charts { display: grid; gap: 8px; align-content: start; }
  /* Both tables span their column; Budget Divisi stretches to the trajectory
     table's height so the row closes flush. */
  .modal-body.sec-4 :is(.ga-chart, .ga-a) { align-self: stretch; }
  .modal-body.sec-4 .ga-chart { grid-template-rows: 1fr; }
  .modal-body.sec-4 .ga-chart > .modal-section,
  .modal-body.sec-4 .ga-a { display: grid; grid-template-rows: auto 1fr; }
  .modal-body.sec-4 :is(.ga-chart, .ga-a) .modal-table-wrap { height: 100%; }
  .modal-body.sec-4 :is(.ga-chart, .ga-a) .modal-table { width: 100%; height: 100%; }
  .modal-body.sec-3 .ga-chart { align-self: start; }
  .modal-body.sec-3 .ga-chart .modal-table { width: 100%; }

  /* §5 — hero / trend chart / findings / attendance / Brand Promise. No
     card-face chart for §5 (scorecard, not a plot) — the modal's own trend
     chart is the visual. */
  .modal-body.sec-5 {
    grid-template-columns: 1fr 1fr;
    grid-template-areas:
      "hero  hero"
      "chart chart"
      "b     b"
      "f     f"
      "c     d";
  }
  /* Brand Promise 45 moved here from §9 (2026-11-04): chart beside its
     per-outlet table, stretched together so the table fills the chart height. */
  .modal-body.sec-5 .ga-c [data-chart] { height: 320px; min-height: 280px; }
  .modal-body.sec-5 .ga-d { align-self: stretch; grid-template-rows: auto 1fr; }
  .modal-body.sec-5 .ga-d .modal-table-wrap { height: 100%; }
  .modal-body.sec-5 .ga-d .modal-table { height: 100%; width: 100%; }

  /* §6 — hero (mix rail) / jobs-trend beside the Komposisi table; the
     barber-request-per-outlet spread runs the full width below (2026-09-07:
     Nilai per Job per Layanan was dropped entirely on request, freeing that
     row's height for the outlet spread instead - it stretches into it the
     same way sec-9's chart+table stretch into freed space). The insight-badge
     verdict block was dropped in an earlier pass for the same reason: with a
     full table and a per-outlet spread already on screen, a text summary
     above them was one more thing to read before reaching the numbers. */
  .modal-body.sec-6 {
    /* Was 1.3fr chart / 1fr table - flipped (2026-09-07) so the enlarged
       Komposisi table (bigger font/padding, on request) has the width its
       five columns need instead of overflowing into a horizontal scroll. */
    grid-template-columns: 1fr 1.4fr;
    /* 2026-09-27: trend chart down the left beside Komposisi Item over
       Komposisi Permintaan; the 8-column per-outlet heatmap gets the full
       width below. The Request Barber bar list (c) was dropped. */
    grid-template-areas:
      "hero  hero"
      "chart a"
      "chart d"
      "e     e"
      "i     i"
      "f     j"
      "h     g";
    row-gap: 10px;
    column-gap: 28px;
  }
  .modal-body.sec-6 :is(.ga-d, .ga-e, .ga-f, .ga-g, .ga-h, .ga-i, .ga-j) { min-width: 0; }
  .modal-body.sec-6 :is(.ga-d, .ga-e, .ga-h, .ga-i, .ga-j) .modal-table { width: 100%; }
  /* Grovv chart sits at the bottom beside the Grovv per-outlet table and takes
     that row's height, so the table no longer spans the whole modal. */
  .modal-body.sec-6 .ga-g { align-self: stretch; grid-template-rows: auto 1fr; }
  .modal-body.sec-6 .ga-g [data-chart] { height: 100%; min-height: 120px; }
  /* Antrian / Treehouse sit side by side; the Grovv table shares the bottom
     row with its chart. Tighter cells and wrapping headers keep them inside
     their columns instead of scrolling sideways. */
  .modal-body.sec-6 :is(.ga-f, .ga-j, .ga-h) .modal-table { width: 100%; font-size: 12px; }
  .modal-body.sec-6 :is(.ga-f, .ga-j, .ga-h) .modal-table :is(th, td) { padding: 3px 7px; white-space: normal; line-height: 1.2; }
  .modal-body.sec-6 :is(.ga-f, .ga-j, .ga-h) .modal-table :is(th, td).num { padding-left: 7px; width: auto; }
  .modal-body.sec-6 :is(.ga-f, .ga-j, .ga-h) .modal-table th { font-size: 11px; letter-spacing: 0; }
  .modal-body.sec-6 :is(.ga-f, .ga-j, .ga-h) .modal-table-wrap { overflow-x: visible; }
  .modal-body.sec-6 :is(.ga-d, .ga-e, .ga-i, .ga-j) .modal-table-wrap { overflow-x: auto; }
  .modal-body.sec-6 .ga-d .modal-table { font-size: 12px; }
  /* Whole section tuned to fit one laptop screen without the body scrolling
     (2026-09-09): the four table/chart rows below the hero were ~130px over a
     864px viewport. Every rule here trims vertical space — nothing restructures. */
  /* Dialog fills the viewport (zoom cancel + explicit height above), but at true
     1:1 scale the content is ~120px taller than the box — these trims close that
     gap so nothing scrolls, monthly or weekly. */
  .modal-body.sec-6 { row-gap: 4px; padding-block: 8px; }
  .modal-body.sec-6 .modal-section h3 { margin-bottom: 2px; }
  .modal-body.sec-6 .ga-a .modal-table { font-size: 12px; }
  .modal-body.sec-6 .ga-a .modal-table :is(th, td) { padding: 6px 12px; }
  .modal-body.sec-6 :is(.ga-d, .ga-e, .ga-i, .ga-j) .modal-table :is(th, td) { padding-block: 1px; line-height: 1.12; }
  .modal-body.sec-6 :is(.ga-d, .ga-e, .ga-i, .ga-j) .modal-table th { padding-block: 2px; }
  .modal-body.sec-6 .ga-chart { align-self: stretch; grid-template-rows: auto 1fr; }
  .modal-body.sec-6 .ga-chart [data-chart] { height: 100%; min-height: 120px; }
  /* Haircut over Treehouse, two panels sharing the leftover height. */
  .modal-body.sec-6 .ga-chart.is-split { grid-template-rows: auto 1fr 1fr auto; }

  /* §7 — hero / chart beside the new-members table / retention-per-outlet
     full width below (2026-09-12, on request — the table used to stack under
     the chart in the left column). Verdict row dropped 2026-09-12 with the
     insight badge under the hero. */
  .modal-body.sec-7 {
    grid-template-columns: 1.7fr 1fr;
    grid-template-areas:
      "hero  hero"
      "chart b"
      "c     a"
      "d     e"
      "h     h"
      "f     f"
      "g     g"
      "i     i";
  }
  /* Review Google and CSAT side by side, equal width, instead of two
     full-width tables stacked. Below 1000px the pair falls back to the
     normal block flow and stacks. */
  .modal-pair {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 22px;
    align-items: start;
  }
  .modal-pair > .modal-section { min-width: 0; }
  .modal-pair .modal-table-wrap,
  .modal-pair .modal-table { width: 100%; }
  /* Fit the pair's columns to the half-width cell instead of letting the
     nowrap cells push the wrapper into a horizontal scroll. */
  .modal-pair .modal-table { table-layout: fixed; }
  .modal-pair .modal-table th,
  .modal-pair .modal-table td {
    padding: 6px 7px;
    font-size: 12px;
    white-space: normal;
    overflow-wrap: anywhere;
  }
  .modal-pair .modal-table th { font-size: 11px; letter-spacing: 0; }
  .modal-pair .modal-table td.num,
  .modal-pair .modal-table th.num { padding-left: 7px; }
  .modal-pair .modal-table th:first-child,
  .modal-pair .modal-table td:first-child { width: 24%; }
  /* The table would otherwise sit content-width, left-aligned in its column —
     stretched full width so it reaches the modal's right edge like every
     other section's tables already do (see sec-2/sec-6/sec-9's own .ga-a). */
  .modal-body.sec-7 .ga-b .modal-table-wrap,
  .modal-body.sec-7 .ga-b .modal-table,
  .modal-body.sec-7 .ga-a .modal-table-wrap,
  .modal-body.sec-7 .ga-a .modal-table,
  .modal-body.sec-7 .ga-e .modal-table-wrap,
  .modal-body.sec-7 .ga-e .modal-table,
  .modal-body.sec-7 .ga-i .modal-table-wrap,
  .modal-body.sec-7 .ga-i .modal-table { width: 100%; }
  /* Member Baru per Outlet gained two compare columns in the narrow §7
     column; fixed layout lets them wrap and fit instead of scrolling sideways. */
  .modal-body.sec-7 .ga-b .modal-table { width: 100%; table-layout: fixed; }
  .modal-body.sec-7 .ga-b .modal-table th,
  .modal-body.sec-7 .ga-b .modal-table td {
    padding: 4px 6px;
    font-size: 11.5px;
    white-space: normal;
    overflow-wrap: anywhere;
  }
  .modal-body.sec-7 .ga-b .modal-table th { font-size: 10.5px; letter-spacing: 0; }
  .modal-body.sec-7 .ga-b .modal-table td.num,
  .modal-body.sec-7 .ga-b .modal-table th.num { padding-left: 6px; }
  .modal-body.sec-7 .ga-b .modal-table th:first-child,
  .modal-body.sec-7 .ga-b .modal-table td:first-child { width: 30%; }
  /* Weekly's Member Baru table is a normal vertical list (30+ rows) instead
     of transposed like monthly's — bounded to the chart's own height and
     scrolled internally, so it can't stretch the whole modal taller the way
     it did before this was beside the chart instead of stacked under it. */
  .modal-body.sec-7 .ga-b .modal-table-wrap {
    max-height: 188px; overflow-y: auto;
    scrollbar-width: none; /* still scrolls — bar hidden, unlike modal-table-wrap's default thin bar */
  }
  .modal-body.sec-7 .ga-b .modal-table-wrap::-webkit-scrollbar { display: none; }
  /* Retention per Outlet (11 rows) beside the retention trend chart — same
     bounded-scroll treatment, now that it sits chart-height instead of
     running the full width of the modal on its own row. */
  .modal-body.sec-7 .ga-a .modal-table-wrap {
    max-height: 188px; overflow-y: auto; scrollbar-width: none;
  }
  .modal-body.sec-7 .ga-a .modal-table-wrap::-webkit-scrollbar { display: none; }
  /* Customer Moving history beside the moving trend chart - same bounded-
     scroll treatment as its neighbours above, so all three tables in this
     section read as one consistent size (2026-09-12). */
  .modal-body.sec-7 .ga-e .modal-table-wrap {
    max-height: 188px; overflow-y: auto; scrollbar-width: none;
  }
  .modal-body.sec-7 .ga-e .modal-table-wrap::-webkit-scrollbar { display: none; }
  /* Sticky header row for these three scrolling tables only (2026-09-12) —
     the header stays put, just the body rows scroll underneath it. The
     header's own background (.modal-table th, opaque brand fill) already
     covers scrolled-under rows, so no extra background rule is needed here. */
  .modal-body.sec-7 .ga-a .modal-table th,
  .modal-body.sec-7 .ga-b .modal-table th,
  .modal-body.sec-7 .ga-e .modal-table th {
    position: sticky;
    top: 0;
    z-index: 1;
  }
  /* Collapsed borders put half the table's top border outside the sticky th's
     box, so rows scrolling under it showed through as a clipped strip of text
     above the header band. Separate borders leave the th flush at the top of
     the scrollport with nothing above it. Borders are single-sided already
     (td border-bottom, td + td border-left), so nothing doubles. */
  .modal-body.sec-7 :is(.ga-a, .ga-b, .ga-e) .modal-table {
    border-collapse: separate;
    border-spacing: 0;
  }

  /* §8 — hero / headcount and rate charts side by side / the turnover table
     full width below. (Was charts stacked beside a short table, which left
     the right column empty under the table.) */
  .modal-body.sec-8 {
    grid-template-columns: 1fr 1fr;
    grid-template-areas:
      "hero  hero"
      "chart b"
      "a     a";
  }
  .modal-body.sec-8 :is(.ga-chart, .ga-b) [data-chart] { height: 240px; min-height: 220px; }
  .modal-body.sec-8 .ga-a .modal-table-wrap,
  .modal-body.sec-8 .ga-a .modal-table { width: 100%; }

  /* §9 — hero (strip / top performer) / chart beside the full table. The
     insight-badge verdict block was dropped (2026-09-07, on request) - the
     hero's own stat cells (biggest shortfall, biggest carrier) already say
     what the verdict text repeated in prose. */
  .modal-body.sec-9 {
    grid-template-columns: 1.05fr 1fr;
    grid-template-areas:
      "hero  hero"
      "chart a"
      "e     e"
      "b     b";
  }
  /* Per-outlet peak-time heatmap (2026-09-15, on request) — same heatmap as
     §7's company-wide one, picked per outlet via the select above it instead
     of showing all 11 at once. Full width, below the chart/table row. */
  .modal-body.sec-9 .ga-b { display: block; }
  :is(.modal-body.sec-9, .modal-body.sec-2) .outlet-peak-picker {
    display: block;
    margin: 0 0 10px;
    padding: 6px 10px;
    font: inherit;
    font-size: 13px;
    color: var(--ink);
    background: var(--field, #fff);
    border: 1px solid var(--line);
  }
  /* The table's row count is fixed (one per outlet) and short of the chart's
     own height, leaving a bare strip under the last row (worse once the
     verdict row above stopped padding it out). Stretched to the chart's full
     height and the table itself justified to fill it - same move sec-2's ga-a
     already makes for the same reason. */
  .modal-body.sec-9 .ga-a { align-self: stretch; grid-template-rows: auto 1fr; }
  .modal-body.sec-9 .ga-a .modal-table-wrap { height: 100%; }
  .modal-body.sec-9 .ga-a .modal-table { height: 100%; width: 100%; }
  /* Was a fixed 300px; now stretches to the row's own height (freed up by
     dropping the hero's distribution strip, 2026-09-07) so the chart claims
     that space instead of leaving it as a gap above the table's own stretch. */
  .modal-body.sec-9 .ga-chart { align-self: stretch; grid-template-rows: auto 1fr; }
  .modal-body.sec-9 .modal-chartwrap [data-chart] { height: 100%; min-height: 280px; }

  /* §10 (2026-09-07, on request): table only — no hero, verdict or chart, so
     no multi-row grid is needed at all. The single "a" area is the table, and
     it gets the full box: the point of dropping the summary blocks was
     reaching all ~20 rows without a scroll, and a leftover multi-row template
     would have left empty hero/verdict/chart bands above it doing that again. */
  .modal-body.sec-10 {
    grid-template-columns: 1fr;
    grid-template-areas: "a";
  }
  .modal-body.sec-10 .ga-a,
  .modal-body.sec-10 .ga-a .modal-table-wrap {
    height: 100%;
  }
  /* The [data-grid] tightening above (11px text, 2px row padding) was sized
     for a table sharing a row with a chart - alone in the whole box it read
     as a small table floating in a lot of empty space. Scaled back up (and
     past sec-1..9's own defaults) since this table is now the ENTIRE modal:
     the row height should be what fills the box, not what a neighbour left
     over. */
  .modal-body.sec-10 .modal-section h3 { font-size: 15px; }
  /* Same override sec-2's own stretched table cell already needed: the page
     doctrine keeps tables content-width (snug beats justified) because that
     reads best floating in a modal with other blocks beside it - alone in the
     whole box, content-width left blank margin on the right instead. */
  .modal-body.sec-10 .modal-table-wrap,
  .modal-body.sec-10 .modal-table { width: 100%; }
  .modal-body.sec-10 .modal-table { font-size: 15px; }
  .modal-body.sec-10 .modal-table :is(th, td) { padding-block: 9px; line-height: 1.3; }
  .modal-body.sec-10 .modal-table th { padding-block: 12px; }
  .modal-body.sec-10 .modal-table :is(th, td).num { padding-left: 28px; }
}

/* One-screen layout above 1000px: the cards already claim the whole viewport,
   so a modal is the only way to see a section at full size. Below 1000px the
   cards already have all the room they need and the click affordance hides,
   so the modal system is desktop-only. */
@media (max-width: 1000px) {
  .dash-card.is-clickable { cursor: default; }
}


/* ===============================================================   Swiss-influenced reskin — 2026-09-09
   ----------------------------------------------------------------------
   Consolidated overrides that don't belong to any one component above.
   The layout math (one-screen grid, --vh scaling, --z zoom) is untouched;
   this only changes surface: corners, rules, the accent, type colour.
   Full rationale: docs/design-swiss.md.
   ====================================================================== */

/* No rounding anywhere. Cards, controls, inputs, pills, meters and the
   section badges are all rectangles — a corner radius is a soft edge this
   world doesn't have. The avatar circle is the one exception (it frames a
   face, not a control). */
.navtoggle,
.nav a,
.navgroup > summary,
.subnav a,
.account .signout,
input, button, select, textarea,
.alert, .notice, .flash, .note, .empty, .placeholder,
.delta,
.mode-toggle, .mode-toggle a,
.period-picker select, .period-picker button, .banding-picker select,
.meter, .meter-fill, .meter-mark,
.swatch,
.dash-card h2 .num,
*::-webkit-scrollbar-thumb {
  border-radius: 0;
}
* { scrollbar-width: none; }

/* The section number: a solid brand square with a white numeral. It was a
   tinted rounded chip; squared and filled it reads as a deliberate index
   mark, and it's the one place the accent carries type. */
.dash-card h2 .num {
  background: var(--brand);
  color: var(--field);
  border: 0;
}

/* The lead KPI chip inverts instead of taking a warm tint — same idiom as
   the active mode toggle, so "this is the headline number" reads once. */
.delta.delta-primary {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--field);
}
.delta.delta-primary strong { color: var(--field); }
.delta.delta-primary.up strong,
.delta.delta-primary.up::after { color: #7ee2a0; }

/* Card titles: the tracking was loose enough to shimmer at meeting distance.
   Tighten it and drop the weight a notch — a grotesque caps line carries
   at 600, and 700 + 0.06em was doing the job of a rule. */
.dash-card h2 {
  font-weight: 600;
  letter-spacing: 0.04em;
}

/* Chart captions and legends: same neutral grey, same size step, so every
   annotation on the page reads as one voice. */
.chart-cap { font-weight: 600; letter-spacing: 0.01em; }

/* Meters keep the accent fill; the track goes to a flat neutral. */
.meter { background: var(--line-soft); }

/* The nav toggle and sidebar controls: square, hairline, no hover tint
   shift toward the accent — hover is a flat grey wash. */
.navtoggle:hover { background: var(--bg); color: var(--ink); }
.nav a:hover { background: var(--bg); color: var(--ink); }

/* --- Dashboard Outlet (OUTLET role) ------------------------------------- */
/* width, not just max-width: auto margins make a grid item shrink to its content. */
.page.dash-outlet { max-width: none; width: 100%; box-sizing: border-box; }
/* One screen at every size, like Company's: the page never scrolls, a card that
   outgrows its cell scrolls inside itself. The shell is pinned to the screen and
   the page takes what the nav strip leaves (phones stack the strip on top, so
   flex there, not the desktop grid). dvh, not vh: a phone's vh counts the space
   under its URL bar, which is the leftover scroll. */
.shell:has(> .dash-outlet) { height: calc(100dvh / var(--z)); min-height: 0; overflow: hidden; grid-template-rows: minmax(0, 1fr); }
@media (max-width: 860px) { .shell:has(> .dash-outlet) { display: flex; flex-direction: column; } }
.page.dash-outlet { display: flex; flex-direction: column; flex: 1; min-height: 0; overflow: hidden; }
.dash-outlet .dash-head { flex: none; }
.dash-outlet .grid { grid-template-columns: minmax(0, 1fr); flex: 1; min-height: 0; grid-auto-rows: minmax(0, 1fr); align-items: stretch; }
.dash-outlet .dash-card { min-height: 0; overflow: hidden; display: flex; flex-direction: column; }
/* The card body the page script zooms to fit (outlet.html); the title stays put. */
.dash-outlet .fit { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.dash-outlet .dash-card h2 { flex: none; }
/* 2x2 at every width: four stacked slivers zoom to nothing (0.4 and still
   clipped on a phone), four quarters keep a usable shape. */
.dash-outlet .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* Phones: every px of chrome is taken from the zoomed content. */
@media (max-width: 599px) {
  .page.dash-outlet { padding: 10px 8px; }
  .dash-outlet .grid { gap: 8px; }
  .dash-outlet .dash-head { margin-bottom: 8px; padding-bottom: 8px; }
  .dash-outlet .dash-head h1 { font-size: 20px; margin: 0; }
  .dash-outlet .dash-head .scope-line { font-size: 12px; }
  .dash-outlet .dash-card { padding: 10px; }
  .dash-outlet .dash-card h2 { font-size: 12px; padding-bottom: 6px; margin-bottom: 8px; }
}
.dash-outlet .mode-toggle a { padding: 0 14px; }
.dash-outlet .dash-head h1 { font-size: clamp(24px, 2.4vw, 36px); margin-bottom: 4px; }
/* Row flow, overriding the Company one-screen .dash-card .tiles column flow. */
.dash-outlet .dash-card .tiles { grid-auto-flow: row; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px 28px; margin-bottom: 20px; }
/* Desktop: 2x2 — Revenue + Productivity on row one, Leaderboard + Peak on row
   two — each row at least half the screen, so the four cards read large. */
@media (min-width: 1000px) {
  .dash-outlet .grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .dash-outlet .dash-card { padding: 28px; }
  .dash-outlet .dash-card .tiles { grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
  .dash-outlet .dash-card h2 { font-size: 18px; }
  .dash-outlet .dash-card .tile-label { font-size: 15px; }
  .dash-outlet .dash-card .tile-value { font-size: clamp(28px, 2.6vw, 44px); }
  .dash-outlet .outlet-table { font-size: 16px; }
}
.dash-outlet .dash-head { margin-bottom: 20px; }
/* One header row, so the cards get the height: period line left (beside the
   nav button), logo centred, mode toggle right. minmax(0, 1fr) on both sides so
   the two side cells stay equal whatever their content, which is what keeps the
   logo on the page's centre line. With the sidebar hidden the page does not drop
   56px under the fixed nav button; the period line steps sideways around it
   instead (on the line, not the header, so the centre does not move). */
.dash-outlet .dash-head { grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; gap: 8px 20px; padding-bottom: 12px; margin-bottom: 16px; }
.dash-outlet .dash-head-titles { display: contents; }
.dash-outlet .dash-head .scope-line { grid-area: 1 / 1; justify-self: start; }
.dash-outlet .dash-head h1 { grid-area: 1 / 2; margin: 0; line-height: 0; }
.dash-outlet .dash-logo { display: block; height: 56px; width: auto; }
.dash-outlet .dash-head .controls { flex: none; flex-flow: row wrap; justify-content: flex-end; align-items: center; }
/* Month picker: same box as the mode toggle beside it (1px line, 2px inset,
   32px row = 38px outer). Chevron + hover/focus: the global select rule. */
.dash-outlet .period-picker select {
  height: 38px;
  padding: 0 34px 0 14px;
  border: 1px solid var(--line);
  background-color: #fff;
  font-family: inherit;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
}
/* Week picker: a future week shows its slot but cannot be picked. */
.mode-toggle.week-pick span { display: inline-flex; align-items: center; height: 32px; padding: 0 12px; font-size: 13px; color: var(--muted); opacity: .4; }
.nav-off .page.dash-outlet { padding-top: 12px; }
.nav-off .dash-outlet .dash-head .scope-line { margin-left: 46px; }
@media (max-width: 599px) {
  .dash-outlet .dash-head { margin-bottom: 8px; padding-bottom: 8px; }
  .dash-outlet .dash-logo { height: 28px; }
}
/* Leaderboard sort headers: the button carries the arrow, aria-sort the state.
   Arrow before the label, so the label still ends on the numbers' right edge. */
.outlet-table.lb th button { all: unset; cursor: pointer; white-space: nowrap; }
.outlet-table.lb th button:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.outlet-table.lb th[aria-sort] button::before { content: "\2195"; margin-right: 4px; color: var(--muted); opacity: .5; }
.outlet-table.lb th[aria-sort="descending"] button::before { content: "\2193"; color: var(--ink); opacity: 1; }
.outlet-table.lb th[aria-sort="ascending"] button::before { content: "\2191"; color: var(--ink); opacity: 1; }
.outlet-table { width: 100%; border-collapse: collapse; font-size: 14px; font-variant-numeric: tabular-nums; }
.outlet-table caption { text-align: left; font-size: 13px; color: var(--muted); padding-bottom: 6px; }
.outlet-table th, .outlet-table td { padding: 6px 8px; border-bottom: 1px solid var(--line); text-align: left; }
.outlet-table thead th { font-size: 12px; color: var(--muted); font-weight: 600; }
.outlet-table tbody th { font-weight: 400; }
.outlet-table .num { text-align: right; }
/* Revenue chart: lines in a stretched SVG, dots and labels as HTML at the same
   percentages, so it fills any box without distorting text. */
.rev-chart {
  display: grid; grid-template-columns: 34px minmax(0, 1fr); grid-template-rows: auto minmax(200px, 1fr) auto;
  grid-template-areas: ". legend" "y plot" ". x"; margin-top: 12px; font-variant-numeric: tabular-nums;
}
.rc-legend { grid-area: legend; display: flex; justify-content: flex-end; gap: 16px; font-size: 12px; color: var(--muted); margin-bottom: 6px; }
.rc-legend span::before { content: ""; display: inline-block; width: 18px; vertical-align: middle; margin-right: 6px; }
.rc-key-actual::before { border-top: 2.5px solid var(--s1); }
.rc-key-target::before { border-top: 1.5px dashed var(--ink); opacity: 0.7; }
.rc-y { grid-area: y; position: relative; }
.rc-y span { position: absolute; right: 8px; transform: translateY(-50%); font-size: 12px; color: var(--muted); }
.rc-plot { grid-area: plot; position: relative; }
.rc-plot svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.rc-plot line { stroke: var(--line-soft); stroke-width: 1; vector-effect: non-scaling-stroke; }
.rc-plot .actual { fill: none; stroke: var(--s1); stroke-width: 2.5; stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.rc-plot .target { fill: none; stroke: var(--ink); stroke-width: 1.5; stroke-dasharray: 6 5; opacity: 0.7; vector-effect: non-scaling-stroke; }
.rc-dot, .rc-tdot, .rc-val { position: absolute; transform: translate(-50%, -50%); }
.rc-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--s1); }
/* The running month/week is not finished — a hollow dot, so its dip reads as "so far". */
.rc-dot.running { background: var(--bg, #fff); border: 2px solid var(--s1); box-sizing: border-box; }
.rc-tdot { width: 5px; height: 5px; border-radius: 50%; background: var(--ink); opacity: 0.5; }
.rc-val { transform: translate(-50%, calc(-100% - 8px)); font-size: 12px; font-weight: 700; color: var(--ink); white-space: nowrap; }
.rc-x { grid-area: x; position: relative; height: 34px; }
.rc-x span { position: absolute; top: 6px; transform: translateX(-50%); text-align: center; font-size: 12px; color: var(--muted); line-height: 1.2; white-space: nowrap; }
.rc-x b { display: block; font-weight: 600; color: var(--ink); }
.rc-x small { font-size: 12px; }
.rc-x .running b::after { content: "*"; }
/* The chart fills what card 1 has left; below its floor the card zooms down. */
.dash-outlet .rev-card .rev-chart { flex: 1 1 0; min-height: 140px; grid-template-rows: auto minmax(0, 1fr) auto; }
.dash-outlet .rev-card .tiles { margin-bottom: 8px; }
@media (min-width: 1000px) {
  .dash-outlet .rev-card .rev-chart { min-height: 200px; }
  /* Four figures on one row, so the chart keeps the card's height. */
  .dash-outlet .rev-card .tiles { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px 20px; }
  .dash-outlet .rev-card .tile-value { font-size: clamp(20px, 1.7vw, 34px); }
  .dash-outlet .rev-card .tile-label, .dash-outlet .rev-card .tile-foot { font-size: 13px; }
}
/* Separation (2026-09-25): a hairline under each card title, and the figures in
   a ruled scorecard — Company §4–§8's language. Tiles overlap by their 1px
   border instead of the §5 gap-on-grey trick, so a short last row leaves blank
   white, not grey cells. */
.dash-outlet .dash-card h2 { padding-bottom: 12px; margin-bottom: 16px; border-bottom: 1px solid var(--line); }
.dash-outlet .dash-card .tiles { gap: 0; padding: 0 1px 1px 0; }
.dash-outlet .dash-card .tiles .tile { padding: 12px 14px; border: 1px solid var(--line); margin: 0 -1px -1px 0; }
.dash-outlet .dash-card .tile-label { display: block; margin-bottom: 4px; }
/* Productivity: always 2x2, like Company §6. */
.dash-outlet .dash-card:not(.rev-card) .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* A card never scrolls (see .fit); its blocks never squash into each other (the peak
   foot rode up over the heatmap). The chart still grows, from a 0 basis. */
.dash-outlet .fit > * { flex-shrink: 0; }
/* A figure never breaks mid-number; it scales with the row it sits in instead. */
.dash-outlet .rev-card .tiles { container-type: inline-size; }
.dash-outlet .dash-card .tile-value { white-space: nowrap; }
/* Too short for one screen at 1x (outlet.html's script sets this): cards take
   their natural height and the page scrolls, rather than shrinking the text. */
.shell:has(> .dash-outlet.is-scroll) { height: auto; overflow: visible; }
.page.dash-outlet.is-scroll { overflow: visible; }
.dash-outlet.is-scroll .grid { grid-auto-rows: auto; }
.dash-outlet.is-scroll .dash-card { overflow: visible; }
@media (min-width: 1000px) {
  .dash-outlet .dash-card .tiles .tile { padding: 14px 16px; }
  .dash-outlet .rev-card .tiles .tile { padding: 12px; }
  .dash-outlet .rev-card .tile-value { font-size: clamp(15px, 3.7cqi, 32px); }
}
/* Phones and tablets: every card is a quarter of the screen, so trim the chrome. */
@media (max-width: 999px) {
  .dash-outlet .dash-card { padding: 14px 16px; }
  .dash-outlet .dash-card h2 { padding-bottom: 8px; margin-bottom: 10px; }
  .dash-outlet .dash-card .tiles .tile { padding: 8px 10px; }
}
/* Shift recommendation (2026-10-14): days across, shift codes down, so a whole week
   fits the quarter card with no scroll. Each count carries a pip per barber, so a
   heavy shift reads as a longer bar before the digits are read; today gets the brand
   rule, past days fade. The week switcher sits in the title row. */
.dash-outlet .shift-head {
  display: flex; flex: none; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 16px;
  padding-bottom: 10px; margin-bottom: 10px; border-bottom: 1px solid var(--line);
}
.dash-outlet .shift-head h2 { margin: 0; padding: 0; border: 0; }
.shift-weeks { display: flex; border: 1px solid var(--line); }
.shift-weeks button {
  display: flex; align-items: baseline; gap: 5px; min-height: 0; padding: 4px 9px; border: 0; border-left: 1px solid var(--line-soft);
  background: none; font: inherit; line-height: 1.15; text-align: left; color: var(--muted); cursor: pointer;
}
.shift-weeks button:first-child { border-left: 0; }
.shift-weeks button b { font-size: 13px; color: var(--ink); }
.shift-weeks button small { font-size: 11px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.shift-weeks button:hover b { color: var(--brand); }
.shift-weeks button[aria-pressed="true"] { background: var(--brand); color: rgba(255, 255, 255, .75); }
.shift-weeks button[aria-pressed="true"] b { color: #fff; }

/* Day picker: one button per day of the week, with how many come in. A dot marks a day
   with overtime or a short hour. */
.shift-days { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); flex: none; border-bottom: 1px solid var(--ink); }
.shift-days [hidden] { display: none; }
.shift-days button {
  position: relative; display: grid; justify-items: center; gap: 1px; min-height: 0; padding: 4px 2px 6px;
  border: 0; background: none; font: inherit; color: var(--ink); cursor: pointer; font-variant-numeric: tabular-nums;
}
.shift-days .dow { font-size: 10px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.shift-days b { font-size: 18px; line-height: 1.1; }
.shift-days small { font-size: 11px; color: var(--muted); }
.shift-days .star { position: absolute; top: 3px; right: 6px; font-size: 10px; color: var(--s3); }
.shift-days .dot { position: absolute; top: 6px; left: 6px; width: 5px; height: 5px; background: var(--brand); }
.shift-days .is-past { opacity: .45; }
.shift-days button:hover b { color: var(--brand); }
.shift-days button[aria-pressed="true"] { background: var(--brand); opacity: 1; }
.shift-days button[aria-pressed="true"] :is(.dow, b, small, .star) { color: #fff; }
.shift-days button[aria-pressed="true"] .dot { background: #fff; }

/* The day: a 07:00–21:00 clock across, one row per barber. Rows share the card's spare height,
   so the bars grow instead of leaving white below. Positions are minutes after midnight. */
.shift-day { flex: 1 0 auto; display: flex; flex-direction: column; padding-top: 6px; }
.shift-day[hidden] { display: none; }
.shift-axis { position: relative; height: 16px; font-size: 10px; color: var(--muted); font-variant-numeric: tabular-nums; }
.shift-axis span { position: absolute; left: calc((var(--h) - 7) / 14 * 100%); transform: translateX(-50%); }
.shift-axis span:first-child { transform: none; }
.shift-axis span:last-child { transform: translateX(-100%); }
.shift-bars {
  flex: 1 0 auto; display: grid; grid-auto-rows: minmax(22px, 1fr); gap: 4px; margin: 0; padding: 4px 0; list-style: none;
  /* a hairline every clock hour */
  background: repeating-linear-gradient(90deg, var(--line-soft) 0 1px, transparent 1px calc(100% / 14));
  border-right: 1px solid var(--line-soft);
}
.shift-bars li { position: relative; min-height: 22px; }
.shift-bars .seg {
  position: absolute; top: 0; bottom: 0;
  left: calc((var(--a) - 420) / 840 * 100%); width: calc((var(--b) - var(--a)) / 840 * 100%);
  display: flex; align-items: center; gap: 6px; padding: 0 8px; overflow: hidden; white-space: nowrap;
  background: #efe4de; box-shadow: inset 3px 0 0 var(--brand); color: var(--brand-dark); font-size: 12px; font-variant-numeric: tabular-nums;
}
.shift-bars .seg b { font-weight: 700; }
.shift-bars .seg > span { color: var(--ink); }
/* Overtime: the same bar, striped, on the end it stretches. */
.shift-bars .seg.ot {
  padding: 0 4px; font-size: 10px; font-weight: 700; color: var(--brand);
  background: repeating-linear-gradient(135deg, #e9d9d1 0 4px, #fff 4px 7px); box-shadow: inset 0 0 0 1px var(--brand);
}
.shift-sum { display: flex; flex-wrap: wrap; gap: 4px 16px; margin: 8px 0 0; font-size: 13px; color: var(--muted); }
.shift-sum .lembur { display: inline-flex; align-items: center; gap: 6px; color: var(--brand); font-weight: 600; }
.shift-sum .key { width: 18px; height: 10px; background: repeating-linear-gradient(135deg, #e9d9d1 0 4px, #fff 4px 7px); box-shadow: inset 0 0 0 1px var(--brand); }
.shift-sum .kurang { color: #b3261e; font-weight: 600; }
@media (max-width: 599px) {
  .shift-weeks button { padding: 3px 6px; }
  .shift-weeks button small { display: none; }
  .shift-days small { display: none; }
  .shift-bars .seg { font-size: 10px; padding: 0 4px; }
  .shift-bars .seg > span { display: none; }
}

/* Outlet Productivity (2026-09-25 redesign): one lead figure on a capacity meter,
   three small stats on a rule, and the job mix as share bars — replacing a 2x2
   of equal-weight boxes and a flat list whose zero rows weighed as much as 1.039. */
/* Lead and stats share a row: the meter on the left, three label/value lines
   on the right, so the mix below gets the height. */
.dash-outlet .prod-top { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr); gap: 0 24px; padding-bottom: 14px; margin-bottom: 14px; border-bottom: 1px solid var(--line); }
.dash-outlet .prod-lead { display: flex; flex-direction: column; gap: 4px; }
.dash-outlet .prod-lead-value { font-size: clamp(34px, 3.4vw, 56px); line-height: 1.05; font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.dash-outlet .prod-lead-value.neg { color: #a3301a; }
.dash-outlet .prod-lead-value.pos { color: #1e6b33; }
.dash-outlet .prod-lead-value .tile-den { color: var(--muted); font-weight: 400; font-size: 0.55em; }
.prod-meter { position: relative; height: 10px; background: var(--line-soft); margin: 6px 0 2px; }
.prod-meter i { display: block; height: 100%; max-width: 100%; background: #a3301a; }
.prod-meter i.pos { background: #1e6b33; }
/* The target tick overhangs the track so it reads even where the bar passes it. */
.prod-meter b { position: absolute; top: -4px; bottom: -4px; width: 2px; margin-left: -1px; background: var(--ink); }
.dash-outlet .prod-stats { display: flex; flex-direction: column; justify-content: center; border-left: 1px solid var(--line); padding-left: 24px; }
.dash-outlet .prod-stats > * { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; padding: 6px 0; }
.dash-outlet .prod-stats > * + * { border-top: 1px solid var(--line-soft); }
.dash-outlet .prod-stats .tile-label { margin: 0; }
.dash-outlet .prod-stats strong { font-size: clamp(18px, 1.6vw, 26px); font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.dash-outlet .prod-stats .tile-foot { flex-basis: 100%; }
.dash-outlet .prod-stats > div:has(.tile-foot) { flex-wrap: wrap; }
/* Antrian / Customer Pulang rows are buttons that open their log; look like the other rows, plus a hover. */
.dash-outlet .prod-stat-btn {
  width: 100%; min-height: 0; margin: 0; background: none; color: var(--ink);
  border: 0; border-radius: 0; font: inherit; text-align: left; cursor: pointer;
}
.dash-outlet .prod-stat-btn:hover .tile-label { color: var(--ink); text-decoration: underline; }
table.queue-log-table { width: 100%; border-collapse: collapse; font-size: 14px; }
table.queue-log-table th, table.queue-log-table td { padding: 10px 8px; border-bottom: 1px solid var(--line-soft); text-align: left; vertical-align: top; }
table.queue-log-table thead th { font-size: 11px; font-weight: 650; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); border-bottom: 1px solid var(--line); }
/* Wider than the default drawer: four short columns plus a free-text reason.
   Short columns hug their content (width 1% + nowrap) so the reason gets the rest. */
dialog.drawer.queue-log { width: min(760px, 100vw); }
table.queue-log-table thead th { white-space: nowrap; }
table.queue-log-table td:not(:last-child) { width: 1%; white-space: nowrap; }
table.queue-log-table td:last-child { min-width: 16ch; }
@media (max-width: 600px) { table.queue-log-table thead th { white-space: normal; } }
table.queue-log-table .num { text-align: right; font-variant-numeric: tabular-nums; }
table.queue-log-table tfoot th, table.queue-log-table tfoot td { font-weight: 700; border-top: 1px solid var(--line); border-bottom: 0; }
.prod-mix-cap { margin: 0 0 6px; font-size: 13px; color: var(--muted); }
/* Grovv units under the OR meter: caption, an optional "kumulatif" note, then
   one name/count line per variant — the job-mix rhythm without the share column. */
.dash-outlet .prod-grovv { margin-top: 14px; }
.prod-grovv-note { margin: -2px 0 6px; font-size: 12px; color: var(--muted); }
.prod-grovv-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: baseline; gap: 10px; padding: 5px 0; border-bottom: 1px solid var(--line-soft); font-size: 15px; font-variant-numeric: tabular-nums; }
.prod-grovv-row:last-child { border-bottom: 0; }
.prod-grovv-row strong { font-weight: 700; }
/* Two columns of three: name, count and share on one line, so the number sits
   next to its name instead of across the card. */
.prod-mix-rows { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 28px; }
.prod-mix-row { display: grid; grid-template-columns: minmax(0, 1fr) auto 3.6em; align-items: baseline; gap: 10px; padding: 6px 0; border-bottom: 1px solid var(--line-soft); font-size: 15px; font-variant-numeric: tabular-nums; }
.prod-mix-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.prod-mix-num { text-align: right; font-weight: 700; }
.prod-mix-pct { text-align: right; color: var(--muted); font-size: 0.87em; }
.prod-mix-row.is-zero { color: var(--muted); }
.prod-mix-row.is-zero .prod-mix-num { font-weight: 400; }

/* Marketing dashboard (marketing.html) — numbered rows of KPI tiles. Same
   Swiss chrome as the Company cards: hairline boxes, radius 0, the brand badge
   for the row number. Seven tiles in one line on a wide screen, reflowing
   below that; the foot rule sits at the bottom of every tile whatever the
   sub-line's length, so the feet line up across the row. */
.mkt-row { margin-bottom: 14px; }
/* Sidebar hidden: `.nav-off .dash` drops the 56px that clears the fixed
   .navtoggle (12px + 36px tall), trusting Company's delta chips to step around
   it. This header has no chips, so the tiles would slide under the button.
   Grow the header just past the button instead of padding the page — title
   and picker stay put, only the tiles move down. */
.nav-off .page.dash.mkt .dash-head { min-height: calc(56px - var(--pad, 0px)); }
.mkt-row-head { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.mkt-row-head .num {
  display: grid; place-items: center; width: 36px; height: 36px; flex: 0 0 36px;
  background: #f3e4dc; color: var(--brand); border: 1px solid #e8d0c2;
  font-size: 15px; font-weight: 700; font-variant-numeric: tabular-nums;
}
.mkt-row-head h2 { margin: 0; font-size: 18px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; }
.mkt-row-head p { margin: 2px 0 0; font-size: 13px; color: var(--muted); }
.mkt-tiles { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
.mkt-tile {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  padding: 16px 12px 12px; background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
}
.mkt-tile .tile-label { margin-bottom: 8px; font-size: 12px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; }
.mkt-tile .tile-value { font-size: clamp(19px, 1.7vw, 26px); font-variant-numeric: tabular-nums; }
.mkt-tile .tile-value.buruk { color: #a3301a; }
.mkt-tile-note { margin: -12px 0 14px; font-size: 11px; color: var(--muted); font-style: italic; }
/* Header right cell: pencil, then the period controls. The wrapper takes the
   grid cell .controls normally owns. */
.mkt .dash-head .mkt-head-right { grid-area: 1 / 3; justify-self: end; display: flex; align-items: flex-start; gap: 10px; }
.mkt .dash-head .mkt-head-right .controls { grid-area: auto; }
.mkt-edit {
  display: grid; place-items: center; width: 32px; height: 32px; padding: 0; flex: 0 0 32px;
  min-height: 0;   /* the global button's 44px min stretched it to 32×44, off the selects' 32px row */
  background: #fff; border: 1px solid var(--line); color: var(--muted); cursor: pointer;
}
.mkt-edit:hover { color: var(--brand); }
.mkt-edit[aria-pressed="true"] { background: var(--brand); border-color: var(--brand); color: #fff; }
/* Edit mode: only blocks with a manual figure open up; the rest dim so it is
   plain which ones the pencil changed. Each editable block gets a pencil
   layer laid OVER it (absolute, inset 0) — the whole block is the click
   target and nothing inside it moves; the dialog does the editing. */
.mkt-editing .mkt-tile:not(.mkt-tile-editable) { opacity: .45; }
.mkt-editing .mkt-tile-editable, .mkt-editing .mkt-program { outline: 2px dashed var(--brand); outline-offset: -2px; }
.mkt-tile-editable, .mkt-program { position: relative; }
.mkt-open { display: none; }
.mkt-editing .mkt-open {
  position: absolute; inset: 0; display: grid; place-items: center; width: 100%; padding: 0;
  background: rgb(255 255 255 / 0.55); border: 0; color: var(--brand); cursor: pointer;
}
.mkt-open svg {
  box-sizing: content-box; width: 18px; height: 18px; padding: 10px;
  background: #fff; border: 1px solid var(--brand); border-radius: 50%;
}
.mkt-open:hover svg, .mkt-open:focus-visible svg { background: var(--brand); color: #fff; }
.mkt-edit-actions { display: flex; gap: 6px; }
.mkt-edit-actions button { flex: 1; }
.mkt-tile-sub { margin: 4px 0 16px; font-size: 12px; color: var(--muted); }

/* Marketing Budget row: donut + legend on top, one program column per budget
   line below. Slice colours are the series slots — s1/s4/s2, the same order
   MarketingBudget paints the conic-gradient in; change both together. */
.mkt-budget .k-retention   { --k: var(--s1); }
.mkt-budget .k-branding    { --k: var(--s4); }
.mkt-budget .k-acquisition { --k: var(--s2); }
/* Row 2: three equal panels, one column below ~1100px. */
/* Row 2's three cards share one height — the tallest card's content sets
   it, never the window (filling the window left dead space on tall screens).
   A program list shows six lines, then scrolls inside its column, so a long
   list can't stretch the row. */
.mkt-panels { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(max(340px, calc((100% - 24px) / 3)), 1fr)); /* at most 3 across: span-all rows below would otherwise keep auto-fit's empty tracks */ align-items: stretch; }

.mkt-panel { padding: 16px 20px 20px; background: #fff; border: 1px solid var(--line); border-radius: var(--radius); }
.mkt-budget { display: flex; flex-direction: column; }
.mkt-budget-top { display: flex; flex-wrap: wrap; align-items: center; gap: 16px 24px; margin-bottom: 16px; }
.mkt-donut {
  position: relative; width: 160px; height: 160px; flex: 0 0 160px; border-radius: 50%;
  background: var(--line-soft);
}
.mkt-donut-hole {
  position: absolute; inset: 22%; display: flex; flex-direction: column; align-items: center; justify-content: center;
  background: #fff; border-radius: 50%; text-align: center;
}
.mkt-donut-hole strong { font-size: 22px; font-variant-numeric: tabular-nums; }
.mkt-donut-hole strong.buruk { color: #a3301a; }
.mkt-donut-hole span { font-size: 12px; color: var(--muted); }
.mkt-legend { flex: 1 1 180px; display: grid; gap: 12px; margin: 0; padding: 0; list-style: none; }
.mkt-legend li { display: grid; grid-template-columns: 14px 1fr auto; column-gap: 10px; align-items: baseline; }
.mkt-legend .sw { width: 12px; height: 12px; border-radius: 50%; background: var(--k); align-self: center; }
.mkt-legend .name { font-weight: 700; }
.mkt-legend .pct { font-variant-numeric: tabular-nums; }
.mkt-legend .desc { grid-column: 2 / 4; font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.mkt-budget-note { margin: -4px 0 14px; }
.mkt-programs { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }
.mkt-program { padding-top: 10px; border-top: 3px solid var(--k); min-width: 0; overflow-wrap: anywhere; }
.mkt-program h3 { margin: 0 0 4px; font-size: 12px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; }
.mkt-program-fig { margin: 0 0 10px; font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.mkt-program ul { max-height: calc(6 * 1.6em); overflow-y: auto; scrollbar-width: thin; margin: 0; padding-left: 18px; font-size: 13px; line-height: 1.6; }
.mkt-program .mkt-tile-note { margin: 0; }
/* Dialogs sit inside centred tiles in the DOM; reset what they would inherit. */
.mkt-dialog {
  width: min(420px, calc(100vw - 32px)); padding: 20px; border: 1px solid var(--line); border-radius: var(--radius);
  text-align: left; font-size: 14px; font-weight: 400; letter-spacing: normal; text-transform: none; color: var(--ink);
}
.mkt-vc-dialog { width: min(1180px, calc(100vw - 32px)); }
.mkt-vc-scroll { max-height: calc(100vh - 220px); overflow: auto; margin: 0 0 14px; border: 1px solid var(--line); }
.mkt-vc-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 13px; font-variant-numeric: tabular-nums; }
.mkt-vc-table :is(th, td) { padding: 7px 10px; border-right: 1px solid var(--line-soft); border-bottom: 1px solid var(--line-soft); text-align: right; white-space: nowrap; }
.mkt-vc-table thead th { position: sticky; top: 0; z-index: 2; background: var(--ink); color: #fff; font-size: 11px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; border-color: var(--ink); }
.mkt-vc-table .mkt-vc-name { position: sticky; left: 0; z-index: 1; min-width: 15rem; text-align: left; background: #fff; border-right: 2px solid var(--line); }
.mkt-vc-table thead .mkt-vc-name { z-index: 3; background: var(--ink); }
.mkt-vc-table .mkt-vc-name small { display: block; margin-top: 1px; font-size: 10px; font-weight: 400; color: var(--muted); }
.mkt-vc-table tbody tr:nth-child(even) :is(td, th) { background: var(--line-soft); }
.mkt-vc-table td.zero { color: var(--line); }
.mkt-vc-table .mkt-vc-sum { font-weight: 700; border-left: 2px solid var(--line); background: #fff; }
.mkt-vc-table tbody tr:nth-child(even) .mkt-vc-sum { background: var(--line-soft); }
.mkt-vc-table thead .mkt-vc-sum { background: var(--ink); }
.mkt-vc-table tfoot :is(th, td) { position: sticky; bottom: 0; background: #fff; font-weight: 700; border-top: 2px solid var(--ink); }
.mkt-g-detail { all: unset; margin: 0 auto 0 8px; font-size: 11px; font-weight: 700; color: var(--brand); text-decoration: underline; text-underline-offset: 2px; cursor: pointer; }
.mkt-g-detail:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.mkt-dialog-value { width: 100%; margin-bottom: 14px; font-variant-numeric: tabular-nums; }
.mkt-dialog::backdrop { background: rgb(36 26 21 / 0.4); }
.mkt-dialog h3 { margin: 0 0 12px; font-size: 14px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; }
.mkt-dialog ol { display: grid; gap: 6px; margin: 0 0 10px; padding-left: 22px; }
.mkt-dialog li { display: flex; gap: 6px; }
.mkt-dialog li input { flex: 1; min-width: 0; }
.mkt-dialog-del { width: 32px; flex: 0 0 32px; padding: 0; background: #fff; border: 1px solid var(--line); color: var(--muted); cursor: pointer; }
.mkt-dialog-del:hover { color: #a3301a; }
.mkt-dialog-more { width: 100%; margin-bottom: 14px; }
.mkt-editing .mkt-budget-top { opacity: .45; }
.mkt-tile-foot {
  align-self: stretch; margin-top: auto; padding-top: 10px; border-top: 1px solid var(--line-soft);
  font-size: 12px; color: var(--muted);
}
/* Customer Growth & Retention panel — flat like Company's cards: no box in
   a box, groups split by a hairline, labels small caps over the figure. */
.mkt-g { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 20px; }
.mkt-g-stat { display: grid; grid-template-columns: auto 1fr; align-items: baseline; column-gap: 8px; }
.mkt-g-stat strong { font-size: clamp(19px, 1.7vw, 26px); font-variant-numeric: tabular-nums; }
.mkt-g-label, .mkt-g-split dt, .mkt-g-cols dt, .mkt-g-head {
  font-size: 11px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted);
}
.mkt-g-label { grid-column: 1 / -1; margin-bottom: 2px; }
.mkt-g-pct { font-size: 14px; font-weight: 700; color: var(--brand); font-variant-numeric: tabular-nums; }
.mkt-g-foot { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 0 6px; font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
.mkt-g-split { display: grid; grid-template-columns: 1fr 1fr; gap: 0 20px; margin: 10px 0 0; }
.mkt-g-split div { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.mkt-g-split dd { margin: 0; font-size: 14px; font-weight: 700; font-variant-numeric: tabular-nums; }
.mkt-g-head {
  display: flex; justify-content: space-between; gap: 8px; margin: 12px 0 6px; padding-top: 10px;
  border-top: 1px solid var(--line-soft); color: var(--brand);
}
.mkt-g-rev { color: var(--ink); text-transform: none; letter-spacing: 0; font-variant-numeric: tabular-nums; }
.mkt-g-cols { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0 12px; margin: 0; }
.mkt-g-cols4 { grid-template-columns: repeat(4, 1fr); }
.mkt-g-note { margin: 8px 0 0; font-size: 11px; color: var(--muted); }
.mkt-g-cols2 { grid-template-columns: repeat(2, 1fr); gap: 10px 12px; }
.mkt-g-cols dt { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mkt-g-cols dd { margin: 2px 0 0; font-size: 18px; font-weight: 700; font-variant-numeric: tabular-nums; }
.mkt-g-cols dd small { font-size: 11px; color: var(--brand); }
.mkt-g-cols dd.mkt-g-foot { margin: 0; font-size: 11px; font-weight: 400; }
.mkt-g-foot .tile-foot { margin: 0; font-size: 11px; }

/* Customer Experience panel. Keyed half (CSAT line, three feedback rings,
   NPS) sits in one editable block; complaint half below a hairline head. */
.mkt-cx-manual { display: grid; gap: 12px; }
.mkt-editing .mkt-cx-manual { outline: 2px dashed var(--brand); outline-offset: -2px; }
.mkt-cx-csat { display: flex; align-items: baseline; justify-content: center; gap: 8px; margin: 0;
  font-size: 11px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--brand); }
.mkt-cx-csat strong { font-size: 18px; letter-spacing: 0; color: var(--ink); font-variant-numeric: tabular-nums; }
.mkt-cx-csat .mkt-g-foot { text-transform: none; letter-spacing: 0; font-weight: 400; }
.mkt-cx-rings { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.mkt-cx-ring { display: flex; flex-direction: column; align-items: center; gap: 4px; text-align: center; min-width: 0; }
.mkt-cx-ring .mkt-g-foot { justify-content: center; }
.mkt-cx .mkt-donut { width: 96px; height: 96px; flex: 0 0 96px; }
.mkt-cx .mkt-donut-hole { inset: 16%; }
.mkt-cx .mkt-donut-hole strong { font-size: 18px; }
.mkt-cx .mkt-donut-hole span { font-size: 11px; font-variant-numeric: tabular-nums; }
.mkt-cx-nps { display: grid; grid-template-columns: auto 1fr; align-items: baseline; column-gap: 8px;
  padding: 10px 12px; border: 1px solid var(--line-soft); }
.mkt-cx-nps .mkt-g-label { grid-column: 1 / -1; }
.mkt-cx-nps strong { font-size: clamp(22px, 2vw, 30px); color: #1e6b33; font-variant-numeric: tabular-nums; }
.mkt-cx-form { display: grid; grid-template-columns: auto 1fr 1fr; gap: 8px 10px; align-items: center; margin-bottom: 14px; }
.mkt-cx-form input { width: 100%; min-width: 0; margin: 0; font-variant-numeric: tabular-nums; }
.mkt-cx-top { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 20px; margin-bottom: 12px; }
.mkt-cx-total { display: grid; grid-template-columns: auto; gap: 2px; }
.mkt-cx-total strong { font-size: clamp(22px, 2vw, 30px); font-variant-numeric: tabular-nums; }
.mkt-cx-cat { flex: 1 1 200px; display: flex; align-items: center; gap: 12px; }
.mkt-cx-cat .mkt-donut { width: 80px; height: 80px; flex-basis: 80px; }
.mkt-cx-cat .mkt-donut-hole { inset: 28%; }
.mkt-cx-legend { display: grid; gap: 3px; margin: 0; padding: 0; list-style: none; font-size: 11px; min-width: 0; }
.mkt-cx-legend li { display: grid; grid-template-columns: 10px 1fr auto; gap: 6px; align-items: center; }
.mkt-cx-legend .sw { width: 10px; height: 10px; background: var(--k); }
.mkt-cx-legend .name { font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mkt-cx-legend .pct { color: var(--muted); font-variant-numeric: tabular-nums; }
/* One bar per outlet: fill = complaints (green within the ceiling, red over),
   tick = the ceiling. Same scale for every row. */
.mkt-cx-bars { display: grid; gap: 4px; margin: 0; padding: 0; list-style: none; font-size: 12px; }
.mkt-cx-bars li { display: grid; grid-template-columns: 76px 1fr 32px; gap: 8px; align-items: center; }
.mkt-cx-bars .nama { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mkt-cx-bars .track { position: relative; height: 10px; background: var(--line-soft); }
.mkt-cx-bars .fill { position: absolute; inset: 0 auto 0 0; background: #1e6b33; }
.mkt-cx-bars li.lewat .fill { background: #a3301a; }
.mkt-cx-bars .batas { position: absolute; top: -3px; bottom: -3px; width: 2px; margin-left: -1px; background: var(--ink); }
.mkt-cx-bars .n { text-align: right; font-variant-numeric: tabular-nums; color: var(--muted); }
.mkt-cx-bars li.lewat .n { color: #a3301a; font-weight: 700; }

/* ── Motion + dropdowns (2026-09-27) ─────────────────────────────────────
   Page-to-page: every navigation in this app is a full server round-trip
   (PRD A8), which painted a white frame between pages. A same-origin
   cross-document view transition crossfades instead; the sidebar is named so
   it stays still rather than fading out and back in. Browsers without it
   (Firefox, older Safari) navigate exactly as before. htmx swaps with
   `transition:true` go through the same pseudo-elements. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
  ::view-transition-group(root) { animation-duration: 160ms; }
}
.side { view-transition-name: side; }

/* An htmx swap in flight: dim what is about to be replaced. The delay keeps a
   fast response from flickering at all. */
main.htmx-request { opacity: .55; pointer-events: none; transition: opacity .15s .12s; }

/* One dropdown for every <select>. Each page's own select rule still sets its
   size, border and type; this adds the part they all lacked — one chevron in
   place of the platform arrow, hover and focus states.
   ponytail: !important because eight earlier rules (.period-picker,
   .rate-form, #koreksi-dialog, …) set `background`/`padding` shorthands at a
   higher specificity, and a shorthand wipes the chevron. Cheaper than touching
   all eight; if one select ever needs its own arrow, give it `appearance: auto`. */
select:not([multiple]):not([size]) {
  appearance: none;
  padding-right: 30px !important;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%231a1a1a' stroke-width='1.5'/%3E%3C/svg%3E") !important;
  background-repeat: no-repeat !important;
  background-position: right 12px center !important;
  cursor: pointer;
}
select:hover:not(:disabled) { border-color: var(--ink); }
select:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
select:disabled { cursor: default; }

/* Styled open list where the browser can do it natively (customizable select,
   Chrome/Edge 135+) — same element, same form value, same keyboard handling.
   Mouse/trackpad only: on a phone the OS picker (wheel / sheet) is the better
   control for thumbs, so touch keeps it. Everywhere else: the platform list. */
@media (pointer: fine) {
  @supports (appearance: base-select) {
    select:not([multiple]):not([size]),
    ::picker(select) { appearance: base-select; }
    select::picker-icon { display: none; }            /* the chevron above stands in */
    ::picker(select) {
      margin-top: 2px;
      padding: 4px 0;
      border: 1px solid var(--ink);
      border-radius: 0;
      background: var(--field);
      box-shadow: 0 6px 18px rgb(0 0 0 / .08);
      max-height: 60vh;
    }
    option {
      padding: 6px 28px 6px 12px;
      font-size: 13px;
      color: var(--ink);
    }
    option::checkmark { display: none; }
    option:hover, option:focus-visible { background: var(--line-soft); outline: none; }
    option:checked { font-weight: 700; box-shadow: inset 3px 0 var(--brand); }
    @media (prefers-reduced-motion: no-preference) {
      ::picker(select) {
        transition: opacity .12s, translate .12s, overlay .12s allow-discrete, display .12s allow-discrete;
      }
      ::picker(select):not(:popover-open) { opacity: 0; translate: 0 -4px; }
      @starting-style { ::picker(select):popover-open { opacity: 0; translate: 0 -4px; } }
    }
  }
}

/* Marketing › Data Complain (templates/marketing/complaint.html). Here, not
   inline in the page: a <style> at the end of <main> arrived after the table
   had painted, so rows flashed unclamped with the brown default button. */
/* The page's one primary action, in .page-head beside the title. */
#new-complaint-open, #options-open { width: auto; height: var(--tap); padding: 0 20px; }
.page-actions { display: flex; gap: 10px; }
#options-open, .nc-secondary { background: none; color: var(--ink); box-shadow: inset 0 0 0 1px var(--line); }
#options-open:hover { background: var(--line-soft); }
/* Status tabs: the archive badges, left-aligned above the table and with a count. */
.complaint-tabs { justify-content: flex-start; margin: 0 0 14px; flex-wrap: wrap; }
.complaint-tabs b { margin-left: 6px; font-weight: 700; opacity: .7; }
.complaint-table tbody tr[data-id] { cursor: pointer; }
.complaint-table tbody tr[data-id]:hover { background: var(--line-soft); }
.complaint-table tbody tr[data-id]:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.complaint-count { margin: 0 0 6px; }
/* Operasional / HRD: status tabs and the month picker share one row. */
.complaint-bar { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; flex-wrap: wrap; margin: 0 0 14px; }
.complaint-bar .complaint-tabs { margin: 0; }
.complaint-month { position: relative; margin: 0; }
/* The month picker: a tab-sized button, and a panel like the column filters' (.cf-pop). */
.cm-btn { display: flex; align-items: center; gap: 10px; width: auto; height: 38px; min-height: 0; margin: 0; padding: 0 14px;
  border: 1px solid var(--line); border-radius: 0; background: var(--bg); color: var(--ink);
  font: inherit; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; cursor: pointer; }
.cm-btn:hover, .cm-btn[aria-expanded=true] { background: var(--line-soft); color: var(--ink); }
.cm-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.cm-btn svg { flex: none; width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.8; opacity: .6; }
.cm-pop { position: absolute; top: 100%; right: 0; z-index: 6; width: 232px; padding: 8px; background: #fff;
  border: 1px solid var(--ink); box-shadow: 0 6px 18px rgb(0 0 0 / .12); }
.cm-pop[hidden] { display: none; }
.cm-year { display: flex; align-items: center; justify-content: space-between; margin: 0 0 6px; }
.cm-year b { font-size: 14px; }
.cm-year button { width: 32px; height: 32px; min-height: 0; margin: 0; padding: 0; border: 0; border-radius: 0; background: none;
  color: var(--ink); font-size: 20px; line-height: 1; cursor: pointer; }
.cm-year button:hover:not(:disabled) { background: var(--line-soft); }
.cm-year button:disabled { opacity: .25; cursor: default; }
.cm-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; }
.cm-grid button { height: 36px; min-height: 0; margin: 0; padding: 0; border: 0; border-radius: 0; background: none;
  color: var(--ink); font: inherit; font-size: 13px; cursor: pointer; }
.cm-grid button:hover:not(:disabled) { background: var(--line-soft); }
.cm-grid button.on { background: var(--brand); color: #fff; font-weight: 700; }
.cm-grid button:disabled { color: var(--muted); opacity: .4; cursor: default; }
#new-complaint, #options-dialog, #tl-dialog, #oo-dialog { width: min(760px, calc(100vw - 32px)); padding: 24px 26px; border: 0; border-radius: 0;
                 box-shadow: 0 24px 48px -12px rgba(20, 20, 20, 0.4); zoom: calc(1 / var(--z)); }
#new-complaint::backdrop, #options-dialog::backdrop, #tl-dialog::backdrop, #oo-dialog::backdrop { background: rgba(20, 20, 20, 0.5); }
/* The dialog paints at 1/--z (1.25x on desktop), and under zoom even vh
   units scale, so the cap is multiplied back by --z. Without it the form ran
   past the window's bottom and Simpan could not be reached. */
#new-complaint, #options-dialog, #tl-dialog, #oo-dialog { max-height: calc((100dvh - 32px) * var(--z)); overflow-y: auto; }
/* Long lists (Menu has 13) scroll inside the open dropdown instead of running off
   a short window; the cap follows the window, not the list. */
@supports (appearance: base-select) {
  #new-complaint select::picker(select) { max-height: min(60vh, 320px); overflow-y: auto; overscroll-behavior: contain; }
}
#new-complaint h2, #options-dialog h2, #tl-dialog h2, #oo-dialog h2 { margin: 0 0 16px; font-size: 20px; line-height: 1.2; }
.nc-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px 16px; align-items: start; }
.nc-grid label { display: grid; gap: 6px; margin: 0; font-size: 12px; font-weight: 600; color: var(--muted); }
.nc-grid .nc-wide { grid-column: 1 / -1; }
.nc-grid .nc-span2 { grid-column: span 2; }
/* One box for every control: the global input/select rules give each kind its
   own height, margin and radius, which is what made the rows step. Vertical
   padding, not 0: a base-select <select> (below, pointer: fine) stacks its text
   from the top, so padding is what centres it. */
.nc-grid input, .nc-grid select, .nc-grid textarea {
  width: 100%; height: var(--tap); min-height: 0; margin: 0; padding: 10px 12px; box-sizing: border-box;
  font: inherit; font-size: 14px; font-weight: 400; color: var(--ink);
  background-color: var(--field); border: 1px solid var(--line); border-radius: 0;
}
.nc-grid textarea { height: auto; resize: vertical; }
@media (max-width: 640px) {
  .nc-grid { grid-template-columns: minmax(0, 1fr); }
  .nc-grid .nc-span2 { grid-column: auto; }
}
/* Update kategori: three radios, then the chosen list with a delete per value. */
.opt-kinds { display: flex; gap: 22px; margin: 0 0 14px; }
.opt-kinds label { display: flex; align-items: center; gap: 8px; margin: 0; font-size: 14px; font-weight: 600; cursor: pointer; }
.opt-kinds input { width: auto; height: auto; margin: 0; }
.opt-set { margin: 0 0 12px; padding: 0; border: 1px solid var(--line); max-height: 260px; overflow-y: auto; }
.opt-set ul { margin: 0; padding: 0; list-style: none; }
.opt-set li { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 6px 12px;
              border-bottom: 1px solid var(--line-soft); font-size: 14px; }
.opt-set li:last-child { border-bottom: 0; }
.opt-del { width: 28px; min-height: 0; height: 28px; margin: 0; padding: 0; border: 0; border-radius: 0; background: none;
           color: var(--muted); font-size: 18px; line-height: 1; }
.opt-del:hover, .opt-del:focus-visible { background: var(--line-soft); color: #a3301a; }
.opt-add { display: flex; gap: 8px; margin: 0 0 12px; }
.opt-add[hidden] { display: none; }
.opt-add input { flex: 1; min-width: 0; height: var(--tap); min-height: 0; margin: 0; padding: 10px 12px; box-sizing: border-box;
                 font: inherit; font-size: 14px; color: var(--ink); background-color: var(--field);
                 border: 1px solid var(--line); border-radius: 0; }
.opt-add button { width: auto; height: var(--tap); margin: 0; padding: 0 18px; }
.nc-actions { display: flex; justify-content: flex-end; gap: 10px; margin: 20px 0 0; }
.nc-actions button { width: auto; height: var(--tap); padding: 0 20px; }
.nc-actions .nc-cancel { background: none; color: var(--ink); box-shadow: inset 0 0 0 1px var(--line); }
.nc-actions .nc-cancel:hover { background: var(--line-soft); }
.keluhan-clamp { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3;
                 line-clamp: 3; overflow: hidden; }
.keluhan-more { margin: 4px 0 0; padding: 0; min-height: 0; width: auto; border: 0; background: none; font: inherit;
                font-size: 12px; color: var(--muted); text-decoration: underline; cursor: pointer; }
.keluhan-more:hover, .keluhan-more:focus-visible { background: none; color: var(--ink); }
/* Data Complain: a division's edit button in its own column (Ubah, Tindak lanjut). */
.cell-act { display: block; margin: 6px 0 0; padding: 0; min-height: 0; width: auto; border: 0; background: none;
            font: inherit; font-size: 12px; font-weight: 600; color: var(--brand); cursor: pointer; }
.cell-act:hover, .cell-act:focus-visible { background: none; color: var(--ink); text-decoration: underline; }
#keluhan-dialog { width: min(760px, calc(100vw - 32px)); padding: 24px 26px; border: 0; border-radius: 0;
                  box-shadow: 0 24px 48px -12px rgba(20, 20, 20, 0.4); zoom: calc(1 / var(--z)); }
#keluhan-dialog::backdrop { background: rgba(20, 20, 20, 0.5); }
#keluhan-dialog .keluhan-meta, #tl-dialog .keluhan-meta { margin: 0 0 4px; font-size: 12px; color: var(--muted); }
#keluhan-dialog h2 { margin: 0 0 12px; font-size: 20px; line-height: 1.2; }
#keluhan-dialog .keluhan-full, #tl-dialog .keluhan-full { margin: 0 0 20px; font-size: 15px; line-height: 1.55; white-space: pre-line; }
/* A modal open = the page behind it does not scroll (wheel or touch, including scroll chaining out of the PDF frame). */
html:has(dialog:modal) { overflow: hidden; }
#report-dialog { overscroll-behavior: contain; width: min(980px, calc(100vw - 32px)); padding: 20px 22px; border: 0; border-radius: 0;
                 box-shadow: 0 24px 48px -12px rgba(20, 20, 20, 0.4); zoom: calc(1 / var(--z)); }
#report-dialog::backdrop { background: rgba(20, 20, 20, 0.5); }
#report-dialog .keluhan-meta { margin: 0 0 4px; font-size: 12px; color: var(--muted); }
#report-dialog h2 { margin: 0 0 12px; font-size: 20px; line-height: 1.2; }
#report-dialog iframe { display: block; width: 100%; height: calc((100dvh - 190px) * var(--z)); margin: 0 0 16px; border: 1px solid var(--line); background: #fff; }
.complaint-text { white-space: pre-line; }
/* Widths come from the <colgroup>; fixed layout so streaming rows and filtering
   never re-measure the columns. Below the min-width the .tabel-scroll box scrolls. */
.complaint-table { table-layout: fixed; min-width: 1360px; margin-bottom: 0; }
.complaint-table td { overflow-wrap: anywhere; }
/* Header-as-filter: each <th> is a button that opens a small panel under it. The
   panel is absolute inside the th (position: relative), so it scrolls with the
   table; .tabel-scroll keeps a floor height so a 0-row result cannot clip it. */
.complaint-table th { position: relative; }
.complaint-table th.cf-open { z-index: 5; }
.cf-btn { display: flex; align-items: center; justify-content: space-between; gap: 6px; width: 100%; min-height: 0;
  margin: 0; padding: 0; border: 0; background: none; font: inherit; color: inherit; text-transform: inherit;
  text-align: left; cursor: pointer; }
.cf-btn:hover, .cf-btn:focus-visible { background: none; color: var(--ink); }
.cf-btn svg { flex: none; width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 1.8; opacity: .45; }
.cf-btn:hover svg { opacity: 1; }
th.cf-on { color: var(--brand); }
th.cf-on .cf-btn svg { fill: currentColor; opacity: 1; }
.cf-pop { position: absolute; top: 100%; left: 0; z-index: 6; min-width: 100%; width: max-content; max-width: 260px;
  max-height: 260px; overflow-y: auto; padding: 6px; background: #fff; border: 1px solid var(--ink);
  box-shadow: 0 6px 18px rgb(0 0 0 / .12); text-transform: none; font-weight: 400; }
.cf-pop:has(input) { width: 100%; min-width: 220px; padding: 0; overflow: visible; }
.cf-pop input { width: 100%; height: 32px; min-height: 0; margin: 0; padding: 4px 8px; box-sizing: border-box;
  font: inherit; font-size: 13px; color: var(--ink); background: var(--field); border: 1px solid var(--line); border-radius: 0; }
.cf-pop input { border: 0; }
.cf-pop input:focus-visible { outline: 2px solid var(--brand); outline-offset: 0; }
.cf-pop button { display: block; width: 100%; min-height: 0; margin: 0; padding: 6px 10px; border: 0; border-radius: 0;
  background: none; font: inherit; font-size: 13px; color: var(--ink); text-align: left; cursor: pointer; }
.cf-pop button:hover { background: var(--line-soft); }
.cf-pop button.on { font-weight: 700; box-shadow: inset 3px 0 var(--brand); }
.tabel-scroll:has(.complaint-table) { min-height: 320px; }
/* Date box + calendar button (built by dmy() in complaint.html). The hidden native
   date input sits under the text box so the browser's calendar opens against it. */
.dmy-wrap { position: relative; display: block; }
.dmy-wrap input.dmy { padding-right: 38px; }
.dmy-wrap .dmy-native { position: absolute; left: 0; bottom: 0; width: 100%; height: 0; padding: 0; border: 0; opacity: 0; pointer-events: none; }
.dmy-wrap .dmy-pick { position: absolute; top: 0; right: 0; width: 34px; height: 100%; min-height: 0; margin: 0; padding: 0;
                      display: grid; place-items: center; border: 0; border-radius: 0; background: none; color: var(--muted); }
.dmy-wrap .dmy-pick:hover, .dmy-wrap .dmy-pick:focus-visible { background: var(--line-soft); color: var(--ink); }
.dmy-pick svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
/* Barbertologist search: a .cf-pop panel under the box, as wide as the box. */
.bc-wrap { position: relative; display: block; }
.bc-pop { width: 100%; max-width: none; }
.bc-pop [hidden] { display: none; }
.bc-pop small { margin-left: 8px; color: var(--muted); font-size: 11px; }
/* Tanggal's popover: two dated boxes stacked, then a clear button. */
.cf-pop.cf-range { padding: 8px; }
.cf-range label { display: grid; gap: 4px; margin: 0 0 8px; font-size: 12px; font-weight: 600; color: var(--muted); }
.cf-pop.cf-range input { border: 1px solid var(--line); }
.users-table td.nowrap { white-space: nowrap; }
/* Platform chips, same colours as CS's sheet dropdown. Channels the sheet
   never coloured (Kelola Bisnis, Sampling, Threads) stay grey. */
.plat { display: inline-block; padding: 2px 8px; border-radius: 999px; white-space: nowrap;
        font-size: 12px; background: #e8eaed; color: #3c4043; }
.plat[data-platform="Tiktok"]          { background: #ffe5a0; color: #473821; }
/* Google, Whatsapp CS, Instagram: the brand logo (static/img/*.svg) in front of the name on a white chip,
   and the whole row tinted in the brand's colour (tr[data-platform] in complaint-global.html).
   The ::before is not text, so the header filters still read the plain platform name. */
.plat[data-platform="Google business"],
.plat[data-platform="Whatsapp CS"],
.plat[data-platform="Instagram"] { background: #fff; border: 1px solid var(--pc-line); color: var(--pc-ink); }
.plat[data-platform="Google business"]::before,
.plat[data-platform="Whatsapp CS"]::before,
.plat[data-platform="Instagram"]::before { content: ""; display: inline-block; width: 12px; height: 12px;
        margin-right: 6px; vertical-align: -2px; background: var(--pc-logo) center / contain no-repeat; }
[data-platform="Google business"] { --pc-bar: #4285f4; --pc-tint: #eef4fe; --pc-line: #c6dafc; --pc-ink: #174ea6;
        --pc-logo: url("/img/google-fb25f16e8dc754543fc6c698da38ff66.svg"); }
[data-platform="Whatsapp CS"]     { --pc-bar: #25d366; --pc-tint: #edf9f1; --pc-line: #b9e8c9; --pc-ink: #11743a;
        --pc-logo: url("/img/whatsapp-3af84d4a8e8504e0f5a062c0dee02b26.svg"); }
[data-platform="Instagram"]       { --pc-bar: #c62f94; --pc-tint: #fcf0f8; --pc-line: #f0c4e2; --pc-ink: #8c1f69;
        --pc-logo: url("/img/instagram-3bda9073e7adf6fa0cf6a3d7580ac9ff.svg"); }
.complaint-table tbody tr[data-platform] td { background: var(--pc-tint, transparent); }
.complaint-table tbody tr[data-platform] td:first-child { box-shadow: inset 3px 0 0 var(--pc-bar, transparent); }
.plat[data-platform="Kasir/Outlet"]    { background: #e6cff2; color: #5a3286; }
.plat[data-platform="Moka Pos"]        { background: #0a53a8; color: #fff; }

/* Row dialog's PDFs (Operasional / HRD): the list above the status + upload row. */
#tl-dialog .tl-pdf { margin: 0 0 16px; }
#tl-dialog .tl-pdf-label { display: block; margin: 0 0 6px; font-size: 12px; font-weight: 600; color: var(--muted); }
#tl-dialog .tl-pdf ul { margin: 0; padding: 0; list-style: none; }
#tl-dialog .tl-pdf li { display: flex; align-items: center; gap: 14px; margin: 4px 0; }
#tl-dialog .tl-pdf li form { margin: 0; }
#tl-dialog .tl-pdf .hint { margin: 0; }
#tl-dialog .keluhan-full[data-k=old] { margin-bottom: 8px; font-size: 13px; color: var(--muted); }
/* A division's status on the general Data Complain: green solved, red unsolved, blue on followup, yellow none yet. */
.st { display: block; width: fit-content; margin: 0 0 6px; padding: 2px 8px; border-radius: 999px; white-space: nowrap;
      font-size: 12px; font-weight: 600; }
.st[data-status="Solved"]      { background: #d4edbc; color: #11734b; }
.st[data-status="Unsolved"]    { background: #ffcfc9; color: #b10202; }
.st[data-status="On followup"] { background: #bfe1f6; color: #0a53a8; }
.st[data-status="none"]        { background: #ffe5a0; color: #473821; }
.pdf-badge { display: block; font-size: 12px; font-weight: 600; color: var(--brand); }

/* Data Complain's two datasets (templates/marketing/_complaint-tabs.html): complaints and
   SQ's findings. A view switch under the page head, above the status tabs; Marketing's page
   has only complaints and shows no switch. */
.view-tabs { display: flex; flex-wrap: wrap; gap: 28px; margin: 2px 0 18px; border-bottom: 1px solid var(--line); }
.view-tabs a { margin-bottom: -1px; padding: 8px 0; border-bottom: 2px solid transparent; color: var(--muted);
  font-size: 13px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; text-decoration: none; }
.view-tabs a:hover { color: var(--ink); }
.view-tabs a.on { color: var(--ink); border-bottom-color: var(--brand); }
.view-tabs a:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.view-tabs b { display: inline-block; margin-left: 8px; padding: 1px 7px; border-radius: 999px;
  background: var(--line-soft); color: var(--muted); font-size: 11px; letter-spacing: 0;
  font-variant-numeric: tabular-nums; }
.view-tabs a.on b { background: var(--brand); color: #fff; }

/* Findings SQ Database: the complaint table's shell with its own column floor (fewer columns). */
.findings-table { min-width: 1120px; }
.findings-table.findings-global { min-width: 1240px; }

/* The finding's row dialog: same chrome and body as #tl-dialog, which the complaint view uses. */
#fn-dialog { width: min(760px, calc(100vw - 32px)); padding: 24px 26px; border: 0; border-radius: 0;
  box-shadow: 0 24px 48px -12px rgba(20, 20, 20, 0.4); zoom: calc(1 / var(--z));
  max-height: calc((100dvh - 32px) * var(--z)); overflow-y: auto; }
#fn-dialog::backdrop { background: rgba(20, 20, 20, 0.5); }
#fn-dialog h2 { margin: 0 0 16px; font-size: 20px; line-height: 1.2; }
#fn-dialog .keluhan-meta { margin: 0 0 4px; font-size: 12px; color: var(--muted); }
#fn-dialog .keluhan-full { margin: 0 0 20px; font-size: 15px; line-height: 1.55; white-space: pre-line; }
#fn-dialog .tl-pdf { margin: 0 0 16px; }
#fn-dialog .tl-pdf-label { display: block; margin: 0 0 6px; font-size: 12px; font-weight: 600; color: var(--muted); }
#fn-dialog .tl-pdf ul { margin: 0; padding: 0; list-style: none; }
#fn-dialog .tl-pdf li { display: flex; align-items: center; gap: 14px; margin: 4px 0; }
#fn-dialog .tl-pdf li form { margin: 0; }
#fn-dialog .tl-pdf .hint { margin: 0; }

/* SQ's CCTV input after a finding: the one save that leaves the page's own data, so it says where. */
.sq-filed a { margin-left: 8px; font-weight: 700; color: inherit; }

/* Business Dev › Partnership Candidate (templates/busdev). Swiss rules like the rest of the sheet:
   no radius, hairlines, brown only for the one thing that matters — where a candidate is in the pipeline. */
.bd-filter { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 20px; }
.bd-filter a { display: inline-flex; gap: 8px; align-items: baseline; padding: 6px 12px; border: 1px solid var(--line);
  color: var(--ink); text-decoration: none; font-size: 13px; }
.bd-filter a b { color: var(--muted); font-weight: 600; }
.bd-filter a:hover { border-color: var(--brand); }
.bd-filter a.on { background: var(--brand); border-color: var(--brand); color: #fff; }
.bd-filter a.on b { color: #fff; }
.bd-table { min-width: 1180px; }
.bd-table td { vertical-align: middle; padding-top: 14px; padding-bottom: 14px; }
.bd-table tbody tr { cursor: pointer; }
.bd-table tbody tr:hover { background: var(--line-soft); }
.bd-table a { color: var(--ink); text-decoration: underline dotted; text-underline-offset: 3px; }
.bd-table a:hover { color: var(--brand); text-decoration-style: solid; }
.bd-table a.bd-name { font-weight: 600; text-decoration: none; }
.bd-table a.bd-name:hover { text-decoration: underline; }
.bd-sub { display: block; margin-top: 2px; font-size: 12px; color: var(--muted); }
.bd-nowrap { white-space: nowrap; }
.bd-filters { display: flex; flex-wrap: wrap; gap: 8px 20px; align-items: flex-start; margin: 0 0 20px; }
.bd-filters .bd-filter { margin: 0; }
.bd-pfilter { margin: 0; display: flex; gap: 8px; flex-wrap: wrap; }
.bd-pfilter select { padding: 7px 10px; border: 1px solid var(--line); border-radius: 0; background-color: #fff; font: 13px/1.3 var(--font); }
/* Kelayakan climbs from outline to filled brown; Status is brown while running, filled when won, dark when lost. */
.bd-badge { display: inline-block; padding: 3px 9px; border: 1px solid var(--line); font-size: 12px; font-weight: 600; white-space: nowrap; }
.bd-badge.none { color: var(--muted); font-weight: 400; }
.bd-badge.k-TIDAK_LAYAK { border-color: var(--ink); color: var(--ink); }
.bd-badge.k-LAYAK_CATATAN { border-color: var(--muted); color: var(--ink); }
.bd-badge.k-POTENSIAL { border-color: var(--brand); color: var(--brand); }
.bd-badge.k-SANGAT_POTENSIAL { background: var(--brand); border-color: var(--brand); color: #fff; }
.bd-badge.o-BERJALAN { border-color: var(--brand); color: var(--brand); }
.bd-badge.o-DEAL { background: var(--brand); border-color: var(--brand); color: #fff; }
.bd-badge.o-NO_DEAL { background: var(--ink); border-color: var(--ink); color: #fff; }
.bd-badges { display: flex; gap: 6px; align-items: center; }
.bd-status select:disabled { opacity: 1; cursor: not-allowed; }
.bd-note select { padding: 10px 12px; border: 1px solid var(--line); border-radius: 0; background-color: #fff; font: inherit; }
.bd-note .hint { margin: 0; }
.bd-inline { display: flex; gap: 10px; width: 100%; }
.bd-inline select { flex: 1; min-width: 0; }
.bd-inline button { white-space: nowrap; }
.bd-warn { margin: 0 0 12px; }
.bd-detail > p.hint a { color: var(--muted); text-decoration: none; }
.bd-detail > p.hint a:hover { color: var(--brand); }
.bd-dl a { color: var(--ink); text-decoration: underline dotted; text-underline-offset: 3px; }
.bd-dl a:hover { color: var(--brand); text-decoration-style: solid; }
.bd-off td:not(:last-child) { color: var(--muted); }
.bd-status { margin: 0; display: grid; gap: 7px; }
/* Where the candidate is in the 12 steps, at a glance; grey (not brown) once it is a No Deal. */
.bd-meter { display: grid; grid-template-columns: repeat(12, 1fr); gap: 2px; height: 3px; }
.bd-meter i { background: var(--line); }
.bd-meter i.on { background: var(--brand); }
.bd-meter.drop i.on { background: var(--muted); }
.bd-status select { width: 100%; min-width: 190px; padding: 7px 10px; border: 1px solid var(--brand); border-radius: 0; background-color: #fff;
  color: var(--brand); font: 600 13px/1.3 var(--font); cursor: pointer; }
.bd-status select.deal { background-color: var(--brand); color: #fff; }
/* A filled select needs a white arrow; the global one is near-black. */
.bd-status select.deal:not([multiple]):not([size]) {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23ffffff' stroke-width='1.5'/%3E%3C/svg%3E") !important;
}
.bd-status select.drop { border-color: var(--ink); color: var(--ink); }
.bd-status select:hover { background-color: var(--line-soft); }
.bd-status select.deal:hover { background-color: var(--brand); }
.bd-head { display: flex; align-items: center; justify-content: space-between; gap: 12px 16px; margin: 0 0 20px; }
.bd-head h1 { margin: 0; }
.bd-head .hint { margin: 2px 0 0; }
.bd-wa { padding: 8px 16px; border: 1px solid var(--brand); color: var(--brand); font-size: 13px; font-weight: 600; text-decoration: none; white-space: nowrap; }
.bd-wa:hover { background: var(--brand); color: #fff; }
.bd-back { margin: 0 0 14px; }
.bd-actions { display: flex; flex-wrap: wrap; gap: 8px; }
button.bd-wa { width: auto; min-height: 0; border-radius: 0; background: none; font-family: inherit; cursor: pointer; }
button.bd-del { border-color: var(--line); color: var(--muted); }
button.bd-del:hover, button.bd-del:focus-visible { background: #a3301a; border-color: #a3301a; color: #fff; }
/* The three questions as one band: each cell is a label and one control, saved the moment it changes. */
.bd-state { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); border: 1px solid var(--line); background: #fff; margin: 0 0 20px; }
.bd-cell { display: grid; gap: 10px; align-content: start; padding: 18px 22px 20px; border-left: 1px solid var(--line); margin: 0; }
.bd-cell:first-child { border-left: 0; }
.bd-cell .hint { margin: 0; }
.bd-lbl { font-size: 11px; text-transform: uppercase; letter-spacing: .08em; color: var(--brand); font-weight: 600; }
.bd-cell > .bd-badge, .bd-cell div > .bd-badge { justify-self: start; font-size: 13px; padding: 6px 12px; }
.bd-state select { width: 100%; padding: 10px 12px; border: 1px solid var(--line); border-radius: 0; background-color: #fff; font: 600 14px/1.3 var(--font); cursor: pointer; }
.bd-state .bd-status select { border-color: var(--brand); color: var(--brand); }
.bd-state .bd-status select.deal { color: #fff; }
.bd-state .bd-status select.drop { border-color: var(--ink); color: var(--ink); }
.bd-state select.o-DEAL { background-color: var(--brand); border-color: var(--brand); color: #fff; }
.bd-state select.o-NO_DEAL { background-color: var(--ink); border-color: var(--ink); color: #fff; }
.bd-state select.o-DEAL:not([multiple]):not([size]), .bd-state select.o-NO_DEAL:not([multiple]):not([size]) {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23ffffff' stroke-width='1.5'/%3E%3C/svg%3E") !important;
}
.bd-cellform { display: grid; gap: 10px; margin: 0; }
.bd-reason { display: grid; gap: 8px; justify-items: start; }
.bd-reason[hidden] { display: none; }
.bd-reason textarea { width: 100%; font: inherit; padding: 10px; border: 1px solid var(--line); border-radius: 0; resize: vertical; }
.bd-reason button { width: auto; padding: 8px 18px; }
.bd-cols { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); gap: 20px; align-items: start; }
.bd-col { display: grid; gap: 20px; min-width: 0; }
.bd-card { border: 1px solid var(--line); background: #fff; padding: 18px 22px 22px; }
.bd-h { margin: 0 0 14px; padding: 0 0 8px; border-bottom: 1px solid var(--line); font-size: 12px; text-transform: uppercase;
  letter-spacing: .08em; color: var(--brand); }
.bd-dl { display: grid; grid-template-columns: 1fr 1fr; gap: 16px 28px; margin: 0; }
.bd-dl > div { min-width: 0; }
.bd-dl > .wide { grid-column: 1 / -1; }
.bd-dl3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.bd-dl3 > div:nth-child(7) { grid-column: span 2; }
.bd-dl dt { margin: 0 0 3px; font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
.bd-dl dd { margin: 0; font-size: 15px; overflow-wrap: anywhere; }
.bd-pre { white-space: pre-line; }
.bd-photos { display: flex; flex-wrap: wrap; gap: 10px; margin: 18px 0 0; }
.bd-photos img { display: block; width: 200px; height: 150px; object-fit: cover; border: 1px solid var(--line); }
.bd-note { display: grid; gap: 10px; justify-items: start; }
.bd-note textarea { width: 100%; font: inherit; padding: 10px; border: 1px solid var(--line); border-radius: 0; resize: vertical; }
.bd-log { list-style: none; margin: 0; padding: 0; }
.bd-log li { display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline; padding: 8px 0; border-bottom: 1px solid var(--line-soft);
  font-size: 14px; }
.bd-log li:last-child { border-bottom: 0; }
@media (max-width: 900px) { .bd-cols, .bd-state { grid-template-columns: 1fr; } .bd-cell { border-left: 0; border-top: 1px solid var(--line); }
  .bd-cell:first-child { border-top: 0; } }
@media (max-width: 560px) { .bd-dl, .bd-dl3 { grid-template-columns: 1fr; } .bd-dl3 > div:nth-child(7) { grid-column: auto; } }
.bd-note button { width: auto; padding: 10px 22px; }
.bd-logfield { font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--brand); }
/* Assessment card: the total big, then the five indicators as bars on the 1-5 scale. */
.bd-total2 { display: flex; align-items: baseline; gap: 10px; margin: 0 0 14px; }
.bd-total2 b { font-size: 40px; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }
.bd-total2 span { font-size: 12px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
.bd-bars { list-style: none; margin: 0 0 16px; padding: 0; display: grid; gap: 10px; }
.bd-bars li { display: grid; grid-template-columns: minmax(0, 190px) 1fr 40px; gap: 12px; align-items: center; font-size: 14px; }
.bd-bars i { display: block; height: 6px; background: var(--line-soft); }
.bd-bars u { display: block; height: 100%; background: var(--brand); }
.bd-bars b { text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; }

/* Proyeksi Revenue (§2 / §9 modal, 2026-10-03): one track 0 → target.
   Filled = revenue already in, light band = Dasar..Optimis landing (Dasar = lowest, red tick; Optimis = highest, green tick),
   ticks = the three landings, dashed rule = target. Status colours only on
   the gap text and the "harus naik" figure. */
.proj-bullets { margin-top: 12px; }
.proj-headline { margin: 0 0 30px; font-size: 17px; color: var(--ink); }
.proj-track { position: relative; height: 30px; background: #f0f0f0; }
.proj-track i { position: absolute; top: 0; bottom: 0; }
.proj-done { left: 0; background: #A8502A; }
.proj-done-label { position: absolute; left: 8px; top: 50%; transform: translateY(-50%);
  font-size: 13px; font-weight: 700; color: #fff; white-space: nowrap; }
.proj-band { background: #A8502A; opacity: 0.32; }
.proj-tick { width: 0; top: -5px !important; bottom: -5px !important; border-left: 2px solid var(--ink); }
.proj-tick.is-low { border-left-color: #a3301a; }
.proj-tick.is-high { border-left-color: #1e6b33; }
.proj-tick.is-dasar { top: -9px !important; bottom: -9px !important; border-left-width: 4px; margin-left: -1px; }
.proj-target { width: 0; top: -12px !important; bottom: -6px !important; border-left: 2px dashed var(--ink); }
.proj-target-tag { position: absolute; bottom: calc(100% + 12px); transform: translateX(-100%); padding-right: 4px;
  white-space: nowrap; font-size: 13px; font-weight: 700; color: var(--ink); }
.proj-figs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-top: 16px; }
.proj-fig { display: grid; border: 1px solid var(--line); padding: 8px 12px; }
.proj-fig.is-dasar { border: 2px solid var(--ink); }
.proj-fig-name { font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--muted); }
.proj-fig-v { font-size: 20px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ink); }
.proj-fig-pct { font-size: 13px; color: var(--muted); font-variant-numeric: tabular-nums; }
.proj-headline .is-short, .proj-need .is-short { color: #a3301a; }
.proj-headline .is-over, .proj-need .is-over { color: #1e6b33; }
.proj-need { margin: 12px 0 0; font-size: 14px; color: var(--ink); }
/* Rincian table spans the modal width like the chart above it. */
.proj-table, .proj-table .modal-table { width: 100%; }
@media (max-width: 560px) {
  .proj-headline { font-size: 15px; }
  .proj-fig-v { font-size: 16px; }
  .proj-fig { padding: 6px 8px; }
}

/* Business Dev › Assessment lokasi (templates/busdev/assessment*.html): the Playbook form, grid sections on
   the left and the live score panel pinned on the right. Same Swiss rules as the list and detail pages. */
.as-cols { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 20px; align-items: start; }
.as-side { position: sticky; top: 16px; display: grid; gap: 12px; }
.as-side button { width: 100%; padding: 12px; }
.as-form .bd-card { padding-bottom: 20px; }
.as-form .hint { margin: 0 0 12px; }
.as-h3 { margin: 22px 0 6px; font-size: 13px; font-weight: 600; }
.as-h3:first-of-type { margin-top: 4px; }
.as-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 16px; margin: 12px 0 0; }
.as-grid > .wide { grid-column: 1 / -1; }
.as-grid label span, .as-form label > span { display: block; margin: 0 0 4px; font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
.as-form input[type=text], .as-form input[type=date], .as-form select {
  width: 100%; box-sizing: border-box; padding: 8px 10px; border: 1px solid var(--line); border-radius: 0; background-color: #fff; font: inherit; font-size: 14px; }
.as-form input.bad, .as-form select.bad { border-color: #b3261e; background-color: #fdf3f2; }
.as-table { width: 100%; min-width: 560px; border-collapse: collapse; margin: 0 0 8px; }
.as-table.as-wide { min-width: 860px; }
.as-table th { padding: 6px 6px 6px 0; text-align: left; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
.as-table td { padding: 3px 6px 3px 0; vertical-align: middle; }
.as-table td.as-n { width: 84px; }
.as-table td.as-c { text-align: center; }
.as-form .as-more { width: auto; margin: 4px 0 0; padding: 6px 12px; font-size: 13px; font-weight: 600; background: #fff; color: var(--ink); border: 1px solid var(--line); }
.as-form .as-more:hover { border-color: var(--brand); color: var(--brand); }
.as-none { display: inline-flex; gap: 8px; align-items: center; margin: 4px 0 0 14px; font-size: 14px; vertical-align: middle; }
.as-form .as-none > span { display: inline; margin: 0; font-size: 14px; letter-spacing: 0; text-transform: none; color: var(--ink); white-space: nowrap; }
.as-form .as-more { vertical-align: middle; }
/* Checkboxes: the grid's input rules are for text fields; left alone they render as 40px boxes. */
.as-form input[type=checkbox] { width: 16px; height: 16px; min-height: 0; margin: 0; padding: 0; flex: none; accent-color: var(--brand); cursor: pointer; }
/* Dropdowns inside a grid keep "— pilih —" on one line. */
.as-table td select { min-width: 9rem; white-space: nowrap; text-overflow: ellipsis; }
.as-table td.as-c { width: 84px; }
.as-sum-t { width: 100%; border-collapse: collapse; }
.as-sum-t th { padding: 0 10px 6px 0; white-space: nowrap; text-align: left; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
.as-sum-t td:nth-child(n+2), .as-sum-t th:nth-child(n+2) { text-align: right; font-variant-numeric: tabular-nums; }
.as-sum-t th:last-child, .as-sum-t td:last-child { padding-right: 0; }
.as-sum-t td { padding: 8px 10px 8px 0; border-top: 1px solid var(--line-soft); vertical-align: top; font-size: 14px; }
.as-total { display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: baseline; margin: 14px 0 0; padding: 12px 0 0; border-top: 1px solid var(--ink); }
.as-total b { font-size: 26px; font-weight: 700; }
.bd-card a.button { display: inline-block; margin: 12px 0 0; padding: 10px 22px; background: var(--brand); color: #fff; text-decoration: none; font-weight: 600; }
.bd-card a.button:hover { background: var(--ink); }
@media (max-width: 1100px) { .as-cols { grid-template-columns: 1fr; } .as-side { position: static; } }
@media (max-width: 560px) { .as-grid { grid-template-columns: 1fr; } }
.bd-ranklink { display: inline-block; padding: 9px 18px; background: var(--brand); border: 1px solid var(--brand); color: #fff; font-size: 13px; font-weight: 600; text-decoration: none; white-space: nowrap; }
.bd-ranklink:hover { background: var(--ink); border-color: var(--ink); }
.bd-ranklink.alt { background: #fff; color: var(--brand); }
.bd-ranklink.alt:hover { background: var(--brand); border-color: var(--brand); color: #fff; }
.bd-rank { width: 56px; font-size: 18px; font-weight: 700; font-variant-numeric: tabular-nums; }
.bd-table .bd-num { text-align: right; font-variant-numeric: tabular-nums; }
.bd-table td.bd-rank { text-align: left; }
.bd-total { font-size: 17px; font-weight: 700; }

/* Business Dev › Parameter Assessment: cards in a grid, label left and input right in each row. */
.bd-ranklinks { display: flex; gap: 10px; }
.bp-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; align-items: start; margin: 0 0 20px; }
.bp-wide { grid-column: 1 / -1; }
.bp-row { display: grid; grid-template-columns: minmax(0, 1fr) 140px; gap: 12px; align-items: center; padding: 6px 0; }
.bp-in { display: flex; align-items: center; gap: 6px; }
.bp-in input { width: 100%; min-width: 0; text-align: right; font-variant-numeric: tabular-nums; }
.bp-in em { font-style: normal; color: var(--muted); font-size: 13px; min-width: 1.5em; }
.bp-bands { min-width: 0; }
.bp-bands th:first-child { width: 28%; }
.bp-err { margin: 8px 0 0; color: #b3261e; font-size: 13px; }
.bp-actions { display: flex; gap: 12px; flex-wrap: wrap; }
.bp-reset { background: #fff; color: var(--ink); border: 1px solid var(--line); }
@media (max-width: 900px) { .bp-grid { grid-template-columns: 1fr; } }
.bp-row label { font-weight: 400; font-size: 14px; }
.bp-form input[type=text] { padding: 7px 10px; font-size: 14px; }
.bp-bands td { padding-right: 10px; }

.as-calc { margin: 14px 0 0; }
.as-calc-t { width: 100%; border-collapse: collapse; }
.as-calc-t th, .as-calc-t td { padding: 6px 10px 6px 0; border-top: 1px solid var(--line-soft); font-size: 14px; text-align: left; font-weight: 400; }
.as-calc-t td:nth-child(2) { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.as-calc-t td.hint { font-size: 12px; color: var(--muted); padding-left: 10px; }
.as-calc-key th, .as-calc-key td:nth-child(2) { font-weight: 700; }
.as-mu { color: var(--muted); font-variant-numeric: tabular-nums; }
/* Computed columns (Middle-Up, per 15 minutes) read as a column of read-only cells the height of the inputs beside them. */
.as-table td.as-mu { width: 112px; }
.as-table tbody td.as-mu > span { display: block; box-sizing: border-box; height: 44px; padding: 0 10px; line-height: 42px; border: 1px solid var(--line-soft); background: #f7f5f2; color: var(--ink); font-size: 14px; text-align: left; }
/* One height for every control in a grid row, so dropdowns sit on the same line as the text fields. */
.as-table td input[type=text], .as-table td input[type=date], .as-table td select { display: block; height: 44px; min-height: 44px; margin: 0; }
.as-foot td.as-n { padding-left: 11px; color: var(--ink); }
.as-foot td { padding-top: 12px; border-top: 1px solid var(--ink); font-weight: 700; font-variant-numeric: tabular-nums; }

/* HRD > Attendance. In the shared sheet, not a <style> at the end of <main>: that one arrived after
   the table had painted, so the page first showed with the global full-width button, the
   cramped title and the oversized inputs, then jumped into place. */
.att-bar { display: flex; align-items: center; justify-content: space-between; gap: 16px; min-height: var(--tap); margin: 0 0 14px; }
.att-bar p { margin: 0; }
.att-bar button { flex: none; width: auto; height: var(--tap); padding: 0 20px; }
.att-head { align-items: flex-start; }
.att-head > div { max-width: 90ch; }
.att-head h1 { margin: 0 0 12px; }
.att-head .hint { margin: 0; }
.att-hit { color: #1e6b33; }
.att-miss { color: #b3261e; }
.att-fig { font-weight: 600; }
/* Fixed layout with a width per column, as on the archive: an auto-layout table re-measures every
   column as its 143 rows arrive, which showed up as the content sliding left and settling back. */
.users-table.att-edit { table-layout: fixed; width: 100%; min-width: 1380px; }
.att-edit th, .att-edit td { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.att-edit .c-no { width: 36px; }
.att-edit .c-nib { width: 85px; }
.att-edit .c-nama { width: 215px; }
.att-edit .c-posisi { width: 135px; }
.att-edit .c-dept { width: 135px; }
.att-edit .c-outlet { width: 175px; }
.att-edit th:nth-child(n+7), .att-edit td:nth-child(n+7) { width: 56px; padding-inline: 2px; text-align: center; overflow: visible; }
.att-edit input[type=number] {
  width: 52px; min-height: 0; margin: 0; padding: .3rem .25rem; text-align: center;
  font-variant-numeric: tabular-nums; -moz-appearance: textfield; appearance: textfield;
}
.att-edit input[type=number]::-webkit-inner-spin-button,
.att-edit input[type=number]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
.att-edit tr.att-fixed td { background: color-mix(in srgb, currentColor 6%, transparent); }
/* The per-row delete/restore control. nth-child(n+7) above sizes it at 56px, which no button fits in. */
.att-edit th.c-act, .att-edit td.c-act { width: 96px; padding-inline: 4px; }
.att-edit tr.att-deleted td { color: color-mix(in srgb, currentColor 45%, transparent); }
.att-edit td.att-deleted-cell { font-style: italic; }
/* Last, so its colour wins over the greyed-out rule above: a plain link-like button, not the global
   filled one — 143 filled brand buttons in one column would be a wall of colour. */
.att-edit td.c-act button {
  width: auto; min-height: 0; height: 28px; padding: 0 8px; border: 1px solid var(--line);
  border-radius: 6px; font-size: 12px; font-weight: 600; color: var(--brand); background: none;
}
.att-edit td.c-act button:hover { background: var(--line-soft); }

/* FAT › Replay Kelola (finance/replay.html) */
.replay-head { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.replay-head h1 { margin-right: auto; }
.replay-tools { margin: 0; }
.replay section { margin-top: 24px; overflow-x: auto; }
.replay td.bad { color: #b3261e; font-weight: 600; }

/* Admin › Impor master Kelola */
.kimport-run { display: flex; gap: 8px; align-items: end; flex-wrap: wrap; margin: 16px 0; }
.kimport td .bad { color: #b3261e; }
.kimport details ul { margin: 4px 0 0; padding-left: 18px; font-size: 13px; }
.stok-steps { margin: 12px 0; padding-left: 20px; line-height: 1.7; }

/* --- Back office tokens (U0, 2026-10-11) ---------------------------------
   Measured from Kelola staging with computed-style probes and their CSS
   bundles (raw dumps: var/ui-ref/u0-*.json, gitignored — never committed).
   Kelola renders at zoom 1 with a 12px rem root, so every value below is a
   painted px size and the back office matches by construction. Their purple
   primary maps to our brown per backoffice-ui-guide.md "Allowed differences";
   every other value is copied, not interpreted. No .bo-* rule may carry a raw
   colour or size that a token here provides. */
@font-face { font-family: "Inter"; font-style: normal; font-weight: 300; font-display: swap;
             src: url("/fonts/inter/Inter-Light-20ebf9409cb5af1a039e38bcb7573896.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 400; font-display: swap;
             src: url("/fonts/inter/Inter-Regular-88ebc17245197678abdc1dae40a150fb.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 500; font-display: swap;
             src: url("/fonts/inter/Inter-Medium-6153bde0a96dec30b9db1519294b0b23.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 600; font-display: swap;
             src: url("/fonts/inter/Inter-SemiBold-73799cb1e67482930884f4ee47be620d.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 700; font-display: swap;
             src: url("/fonts/inter/Inter-Bold-1314b93c0dfc03279e4b872e4bf8dbf8.woff2") format("woff2"); }

:root {
  /* type — Inter is what Kelola actually renders (self-hosted there too,
     weights 300-700, vendored above). Kelola's CircularStd on buttons and
     chrome is a commercial face we cannot ship; Inter stands in — the one
     documented deviation beyond the guide's allowed differences. */
  --bo-font: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
             "Helvetica Neue", Arial, sans-serif;
  --bo-fs-base: 12px;        /* body, cell, input, label */
  --bo-fs-title: 21px;       /* page/brand title (lb-title), weight 500 */
  --bo-fs-dialog: 18px;      /* dialog title, weight 600 */
  --bo-fs-head: 16px;        /* section head (lb-head), weight 600 */
  --bo-fs-th: 12px;          /* table header, weight 700 */
  --bo-fs-tag: 9px;          /* chips/tags (.75rem), weight 700 */
  --bo-lh: 1.5;

  /* neutrals — Kelola's zinc ramp, copied verbatim */
  --bo-text: #3f3f46;
  --bo-text-strong: #18181b;   /* field labels (text-900) */
  --bo-text-head: #172b4d;     /* table headers (their ds-text navy) */
  --bo-text-muted: #71717a;
  --bo-text-nav: #52525b;       /* top-bar tabs and the left-menu title (measured 2026-10-12) */
  --bo-text-faint: #a1a1aa;     /* the flow cards' description text */
  --bo-link: #3b82f6;           /* table links: their blue-500, not the primary */
  --bo-surface: #ffffff;
  --bo-ground: #fafafa;
  --bo-surface-hover: #f4f4f5;
  --bo-field-bg: #f4f5f7;      /* filled select/multiselect (ds-background-input) */
  --bo-field-bg-hover: #e9eaec;
  --bo-border: #e5e7eb;
  --bo-border-input: #e5e7eb;  /* 2px on text inputs */
  --bo-border-table: #dfe1e6;  /* 2px under the table head */
  --bo-border-row: #f4f4f5;    /* 1px between rows */

  /* primary — Kelola's indigo ramp remapped to our brown */
  --bo-primary: #623627;        /* their #4f46e5 */
  --bo-primary-hover: #4b291e;  /* their #433cc3 */
  --bo-primary-pressed: #45261b;/* their #3731a0 */
  --bo-primary-50: #f7f5f4;     /* their #f6f6fe */
  --bo-primary-100: #dcd3cf;    /* their #d5d3f9 */
  --bo-row-hover: #e6dfdc;      /* their #deebfe8c row hover, painted */
  --bo-on-primary: #ffffff;
  --bo-required: #cb3a3a;       /* red-600 required asterisk (semantic, kept) */
  --bo-invalid: #e24c4c;        /* "… tidak boleh kosong" text under a field */
  --bo-invalid-border: #f0a9a7; /* border of the empty required field */

  /* semantic/status colours stay Kelola's (allowed difference 2) */
  --bo-success: #22c55e;
  --bo-success-soft: #dcfce7;
  --bo-success-text: #15803d;
  --bo-info: #3b82f6;
  --bo-warning: #f59e0b;
  --bo-danger: #ef4444;
  --bo-msg-info-bg: #e9e9ff;    --bo-msg-info: #696cff;
  --bo-msg-success-bg: #e4f8f0; --bo-msg-success: #1ea97c;
  --bo-msg-warn-bg: #fff2e2;    --bo-msg-warn: #cc8925;
  --bo-msg-error-bg: #ffe7e6;   --bo-msg-error: #ff5757;

  /* shape — .375rem default, .25rem chips (measured rem × 12px root) */
  --bo-radius: 4.5px;
  --bo-radius-sm: 3px;
  --bo-radius-pill: 10rem;

  /* row/table buttons (kb-splitbutton-table: zinc fill, slate text) and the
     blue membership chip — both verbatim, neither is Kelola's primary */
  --bo-btn-row-bg: #e4e4e7;
  --bo-btn-row-bg-hover: #d4d4d8;
  --bo-btn-row-text: #64748b;
  --bo-chip-bg: #d0e1fd;
  --bo-chip-fg: #326fd1;
  --bo-chip-out-bg: #c7eeea;  /* OUT (outlet) unit chip, teal */
  --bo-chip-out-fg: #119c8d;

  /* elevation — their shadow-1 / shadow-2 / card / dialog, hexes verbatim */
  --bo-shadow-1: 0 3px 5px #00000005, 0 0 2px #0000000d, 0 1px 4px #00000014;
  --bo-shadow-2: 0 4px 10px #00000008, 0 0 2px #0000000f, 0 2px 6px #0000001f;
  --bo-shadow-card: 0 1px 3px #0000001a, 0 1px 2px #0000000f;
  --bo-shadow-dialog: 0 20px 25px -5px #0000001a, 0 8px 10px -6px #0000000a;
  --bo-shadow-pop: 0 10px 15px -3px #0000001a, 0 4px 6px -2px #0000000d; /* datepicker panel */
  --bo-mask: rgba(0, 0, 0, .4);
  --bo-focus: 0 0 0 1px var(--bo-primary);

  /* sizes & spacing */
  --bo-topbar-h: 50px;
  --bo-side-w: 219px;
  --bo-content-pad: 15px;    /* 1.25rem */
  --bo-inline-gap: 6px;      /* .5rem */
  --bo-control-h: 34px;      /* 2.8rem: inputs, selects, buttons */
  --bo-control-h-sm: 24px;   /* 2rem: row buttons */
  --bo-dialog-pad: 18px;
  --bo-row-h: 34px;
}

/* The back office paints at Kelola's scale: --z 1 cancels the dashboard's
   0.8 fit scale (body { zoom: var(--z) } above), so every vh calc dividing
   by --z reads as painted and the px tokens above are literal. */
html:has(.bo-shell) { --z: 1; }

/* Back office (2026-10-09): masters, books, stock. Reskinned to Kelola's look
   in U1/U2 (2026-10-11) — every value below comes from the --bo-* tokens. */
.bo-filter { display: flex; gap: var(--bo-inline-gap); align-items: center; flex-wrap: wrap; margin: 12px 0; }
.bo-filter input, .bo-filter select, .bo-filter button { width: auto; margin: 0; }
.bo-filter input[type=search] { min-width: 240px; flex: 1 1 240px; }
.bo-table td.num, .bo-table th.num { text-align: right; white-space: nowrap; }
.bo-table tr.muted td { color: var(--bo-text-muted); }
.bo-card { border: 1px solid var(--bo-border); border-radius: var(--bo-radius); box-shadow: var(--bo-shadow-card); padding: var(--bo-content-pad); margin: 12px 0; }
.bo-card h2 { margin: 0 0 10px; font-family: var(--bo-font); font-size: var(--bo-fs-head); font-weight: 600; color: var(--bo-text-head); }
.bo-form { display: grid; gap: 10px; max-width: 520px; }
.bo-form label { display: grid; gap: 4px; font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 500; color: var(--bo-text-strong); }
.bo-form.bo-inline { display: flex; flex-wrap: wrap; align-items: end; max-width: none; margin-bottom: 10px; }
.bo-form .bo-check { display: flex; gap: 8px; align-items: center; }
.bo-ok { color: var(--bo-msg-success); }
.linkish { background: none; border: 0; padding: 0; color: var(--bo-danger); text-decoration: underline; cursor: pointer; }
.bo-table tr.bo-group td { font-weight: 600; background: var(--bo-surface-hover); }

/* The scrollbar's space is always reserved, so a page that grows past the fold
   (or a filter that shrinks it) doesn't shift everything sideways. */
html:has(.bo-shell) { scrollbar-gutter: stable; }

/* --- Back office shell (backoffice/layout.html) ---------------------------
   Kelola's layout and measurements (U0 probes, 2026-10-11): a 50px top bar
   with shadow-1 chrome, the sections as tabs, Buat Baru, search and the
   account cluster; the open section's pages in a ~220px left menu. Kelola's
   look throughout: 4.5px radius, soft shadows, our brown where Kelola puts
   its primary. */
.bo-top, .bo-shell {
  font-family: var(--bo-font); font-size: var(--bo-fs-base); line-height: var(--bo-lh); color: var(--bo-text);
}
.bo-top {
  position: sticky; top: 0; z-index: 10;
  display: flex; align-items: stretch;
  height: var(--bo-topbar-h); padding: 0 12px;
  background: var(--bo-surface); box-shadow: var(--bo-shadow-1);
}
/* The brand column is the left menu's width, so the tabs start exactly at the
   menu's right line (--bo-side-w is shared with .bo-side). */
.bo-brand { display: flex; flex: none; align-items: center; gap: 8px; width: var(--bo-side-w); padding-left: 4px; }
.bo-brand img { display: block; height: 30px; width: auto; max-width: calc(var(--bo-side-w) - 16px); }
.bo-tabs { display: flex; overflow-x: auto; flex: none; }
.bo-tabs a, .bo-tabs .off {
  display: flex; flex: none; align-items: center; gap: 6px; padding: 0 14px; white-space: nowrap;
  font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 400; color: var(--bo-text); text-decoration: none;
  border-bottom: 2px solid transparent;
}
.bo-tabs a:hover { color: var(--bo-primary); }
.bo-tabs a.on { color: var(--bo-primary); border-bottom-color: var(--bo-primary); }
.bo-tabs .off, .bo-side .off { color: var(--bo-text-muted); cursor: default; }
.bo-tabs svg { width: 16px; height: 16px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.6; }
.bo-create-btn {
  flex: none; align-self: center; width: auto; min-height: 0; margin: 0 0 0 18px; border: 0; cursor: pointer; display: inline-flex; align-items: center;
  height: var(--bo-control-h); padding: 0 12px;
  font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 700;
  background: var(--bo-primary); color: var(--bo-on-primary); border-radius: var(--bo-radius);
}
.bo-icon::-webkit-details-marker, .bo-avatar::-webkit-details-marker { display: none; }
.bo-create-btn:hover { background: var(--bo-primary-hover); }
.bo-create-btn:active { background: var(--bo-primary-pressed); }
.bo-tools { display: flex; flex: 0 1 auto; min-width: 0; align-items: center; gap: 10px; margin-left: auto; padding-left: 16px; }
.bo-tools > * { flex-shrink: 0; }
.bo-tools > .bo-search { flex-shrink: 1; }
/* Below a wide desktop the shortcut hint goes first; the search box still takes Ctrl+K. */
@media (max-width: 1500px) { .bo-search kbd { display: none; } }
.bo-search {
  display: flex; align-items: center; gap: 7px; flex: 0 1 264px; min-width: 150px;
  height: var(--bo-control-h); padding: 0 8px 0 11px;
  border: 2px solid var(--bo-border-input); border-radius: var(--bo-radius);
  background: var(--bo-surface); color: var(--bo-text-muted);
}
.bo-search:focus-within { border-color: var(--bo-primary); box-shadow: var(--bo-focus); }
.bo-search svg { width: 15px; height: 15px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.8; }
.bo-search input { flex: 1; min-width: 0; min-height: 0; margin: 0; border: 0; outline: 0; box-shadow: none; padding: 0; height: 100%; font: inherit; font-family: var(--bo-font); font-size: var(--bo-fs-base); background: none; }
.bo-search kbd { font: inherit; font-family: var(--bo-font); font-size: 9px; font-weight: 600; padding: 3px 6px; background: var(--bo-surface-hover); color: var(--bo-text-muted); white-space: nowrap; }
.bo-menu { position: relative; }
.bo-icon {
  list-style: none; display: grid; place-items: center; width: 34px; height: 34px; cursor: pointer;
  color: var(--bo-text-muted); border-radius: var(--bo-radius);
}
.bo-icon:hover, .bo-icon.on, .bo-menu[open] > .bo-icon { background: var(--bo-surface-hover); color: var(--bo-primary); }
.bo-icon svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.6; }
.bo-avatar {
  list-style: none; display: grid; place-items: center; width: 30px; height: 30px; margin-left: 6px; cursor: pointer;
  border-radius: 50%; background: var(--bo-primary); color: var(--bo-on-primary);
  font-family: var(--bo-font); font-weight: 600; font-size: var(--bo-fs-base);
}
.bo-pop {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 20; min-width: 220px;
  display: flex; flex-direction: column; padding: 6px 0;
  background: var(--bo-surface); border-radius: var(--bo-radius); box-shadow: var(--bo-shadow-2);
}
.bo-pop > a, .bo-pop > span, .bo-pop > p, .bo-pop > strong, .bo-pop > small, .bo-pop button {
  padding: 8px 14px; margin: 0; font-family: var(--bo-font); font-size: var(--bo-fs-base);
  color: var(--bo-text); text-decoration: none; text-align: left;
}
.bo-pop > small { padding-top: 0; }
.bo-pop > a:hover, .bo-pop button:hover { background: var(--bo-surface-hover); color: var(--bo-primary); }
.bo-pop > .off { color: var(--bo-text-muted); }
.bo-pop > .on::after { content: " ✓"; color: var(--bo-primary); }
.bo-pop form { margin: 0; border-top: 1px solid var(--bo-border); }
.bo-pop button { width: 100%; background: none; border: 0; font: inherit; cursor: pointer; }
.bo-shell { display: flex; align-items: flex-start; }
.bo-side {
  position: sticky; top: var(--bo-topbar-h); flex: 0 0 var(--bo-side-w); align-self: stretch;
  display: flex; flex-direction: column; padding: 12px;
  background: var(--bo-ground);   /* their surface-ground menu column */
  min-height: calc(100vh - var(--bo-topbar-h));
}
.bo-side h2 { font-family: var(--bo-font); font-size: var(--bo-fs-head); font-weight: 600; color: var(--bo-text-head); margin: 0 12px 12px; }
.bo-side a, .bo-side .off {
  display: flex; align-items: center; gap: 10px; height: 42px;
  padding: 0 12px; font-family: var(--bo-font); font-size: var(--bo-fs-base); color: var(--bo-text-muted); text-decoration: none;
  border-radius: var(--bo-radius);
}
.bo-side svg { width: 16px; height: 16px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.6; }
.bo-side .off { color: var(--bo-text-muted); cursor: default; }
.bo-side a:hover { background: var(--bo-surface-hover); color: var(--bo-text); }
/* Kelola marks the open page with a soft primary tint and the primary colour. */
.bo-side a.on { background: var(--bo-primary-50); color: var(--bo-primary); font-weight: 600; }
.bo-shell > main { flex: 1 1 auto; min-width: 0; padding: var(--bo-content-pad) 20px 40px; }
.bo-shell > main.page { max-width: none; }
.bo-shell > main h1 { margin: 0 0 10px; font-family: var(--bo-font); font-size: var(--bo-fs-title); font-weight: 500; color: var(--bo-text); }
/* Page links in the primary colour, like Kelola's accent links, not the browser's
   blue. Component links (a.bo-*) keep their component's own colours. */
.bo-shell > main a:not(.btn):not([class*="bo-"]) { color: var(--bo-primary); text-decoration: none; }
.bo-shell > main a:not(.btn):not([class*="bo-"]):hover { text-decoration: underline; }
/* Row sub-codes read as grey caption text in Kelola, links or not. */
.bo-shell > main .bo-hint a { color: var(--bo-text-muted); }
.bo-shell > main .bo-hint a:hover { color: var(--bo-primary); }

/* Hide/show the left menu: a round button sitting on the menu's right line, like
   Kelola's. Hidden, the button moves to the page's left edge and points back. */
.bo-sidetoggle {
  position: fixed; z-index: 15; top: calc(var(--bo-topbar-h) + 22px); left: calc(var(--bo-side-w) - 15px);
  display: grid; place-items: center; box-sizing: border-box;
  width: 30px; height: 30px; min-width: 0; min-height: 0; aspect-ratio: 1; padding: 0; margin: 0;
  border: 1px solid var(--bo-border); border-radius: 50%; background: var(--bo-surface); color: var(--bo-text); cursor: pointer;
  box-shadow: var(--bo-shadow-2);
}
/* Kelola's hover: the button fills with the accent and the chevron goes white. */
.bo-shell:has(.bo-side:hover) .bo-sidetoggle, .bo-sidetoggle:hover, .bo-sidetoggle:focus-visible {
  background: var(--bo-primary); border-color: var(--bo-primary); color: var(--bo-on-primary);
}
.bo-sidetoggle svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; }
.bo-side-off .bo-side { display: none; }
.bo-side-off .bo-sidetoggle { left: 8px; }
.bo-side-off .bo-sidetoggle svg { transform: scaleX(-1); }
.bo-side-off .bo-shell > main { padding-left: 52px; }

/* Laporan Keuangan: Kelola's breadcrumb, report tabs and labelled filter row. */
.bo-crumb { display: flex; align-items: center; gap: 8px; padding: 4px 0 12px; border-bottom: 1px solid var(--bo-border); font-family: var(--bo-font); font-size: var(--bo-fs-base); color: var(--bo-text); }
.bo-crumb a { display: flex; color: var(--bo-text-muted); }
.bo-crumb a:hover { color: var(--bo-primary); }
.bo-crumb svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.8; }
.bo-crumb strong { font-weight: 600; }
/* Sub-tabs: Kelola's p-tabview — white bar, 2px rule under the row, the open
   tab underlined in the primary. */
.bo-subtabs { display: flex; overflow-x: auto; margin: 12px 0 0; border-bottom: 2px solid var(--bo-border-table); }
.bo-subtabs a, .bo-subtabs .off {
  flex: none; display: flex; align-items: center; height: 44px; padding: 0 15px;
  font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 700; white-space: nowrap; text-decoration: none;
  color: var(--bo-text-muted); border-bottom: 2px solid transparent; margin-bottom: -2px;
}
.bo-shell .bo-subtabs a:not(.on) { color: var(--bo-text-muted); }
.bo-shell .bo-subtabs a:not(.on):hover { color: var(--bo-primary); }
.bo-subtabs a.on { color: var(--bo-primary); border-bottom-color: var(--bo-primary); }
.bo-subtabs .off { color: var(--bo-text-muted); cursor: default; }
.bo-report-filter { display: flex; flex-wrap: wrap; align-items: end; gap: 12px; margin: 12px 0; }
.bo-field { display: grid; gap: 2px; font-family: var(--bo-font); font-size: var(--bo-fs-base); color: var(--bo-primary); }
.bo-field select, .bo-field input {
  box-sizing: border-box; width: auto; min-width: 220px; height: var(--bo-control-h); min-height: 0; margin: 0; padding: 0 9px;
  border: 1px solid var(--bo-field-bg); border-radius: var(--bo-radius); background-color: var(--bo-field-bg);
  font: inherit; font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 400; color: var(--bo-text);
}
.bo-field select:hover, .bo-field input:hover { background-color: var(--bo-field-bg-hover); border-color: var(--bo-field-bg-hover); }
.bo-field select:focus, .bo-field input:focus {
  background-color: var(--bo-surface); border-color: var(--bo-primary); box-shadow: var(--bo-focus); outline: 0;
}
.bo-field select { align-items: center; }
.bo-field.bo-grow { flex: 1 1 260px; }
.bo-field.bo-grow select { width: 100%; }
.bo-report-filter .bo-check { font-weight: 400; display: flex; gap: 6px; align-items: center; height: var(--bo-control-h); font-family: var(--bo-font); font-size: var(--bo-fs-base); white-space: nowrap; }
.bo-check input[type=checkbox] { width: 16px; height: 16px; min-height: 0; margin: 0; flex: none; accent-color: var(--bo-primary); }
.bo-export { margin-left: auto; }
.bo-export summary {
  list-style: none; cursor: pointer; height: var(--bo-control-h); display: flex; align-items: center; justify-content: center;
  gap: 6px; padding: 0 12px; white-space: nowrap;
  border: 1px solid var(--bo-border); border-radius: var(--bo-radius);
  font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 700; color: var(--bo-text);
}
.bo-export summary:hover { border-color: var(--bo-primary); color: var(--bo-primary); }
.bo-shell > main .bo-export .bo-pop a { color: var(--bo-text); }
.bo-report th { white-space: nowrap; text-transform: none; font-family: var(--bo-font); font-size: var(--bo-fs-th); font-weight: 700; color: var(--bo-text-head); padding: 6px; border-bottom: 2px solid var(--bo-border-table); }
.bo-report.bo-table th, .bo-report.bo-table td { border-left: 0; }
.bo-report td { padding: 6px; font-family: var(--bo-font); font-size: var(--bo-fs-base); color: var(--bo-text); border-bottom: 1px solid var(--bo-border-row); }

/* Master pages (Kelola's list look): toolbar, list, Edit button, Aktif switch, modal. */
.bo-flash { margin: 12px 0 0; padding: 15px 21px; border-left: 6px solid transparent; border-radius: 6px; font-family: var(--bo-font); font-size: var(--bo-fs-base); }
.bo-flash.ok { background: var(--bo-msg-success-bg); color: var(--bo-msg-success); border-left-color: var(--bo-msg-success); }
.bo-flash.bad { background: var(--bo-msg-error-bg); color: var(--bo-msg-error); border-left-color: var(--bo-msg-error); }
.bo-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 12px 0; }
.bo-toolbar > button, .bo-toolbar > a { width: auto; margin: 0; }
.bo-toolbar select, .bo-toolbar input, .bo-searchbox input {
  box-sizing: border-box; height: var(--bo-control-h); min-height: 0; margin: 0; padding: 0 9px; width: auto;
  border: 1px solid var(--bo-field-bg); border-radius: var(--bo-radius); background-color: var(--bo-field-bg);
  font: inherit; font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 400; color: var(--bo-text);
}
.bo-toolbar select:hover, .bo-toolbar input:hover, .bo-searchbox input:hover { background-color: var(--bo-field-bg-hover); border-color: var(--bo-field-bg-hover); }
.bo-toolbar select:focus, .bo-toolbar input:focus, .bo-searchbox input:focus {
  background-color: var(--bo-surface); border-color: var(--bo-primary); box-shadow: var(--bo-focus); outline: 0;
}
.bo-toolbar select { flex: 1 1 150px; max-width: 280px; }
.bo-searchbox { display: flex; flex: 0 1 300px; min-width: 200px; }
.bo-searchbox input { flex: 1; min-width: 0; background-color: var(--bo-surface); border: 2px solid var(--bo-border-input); border-radius: var(--bo-radius) 0 0 var(--bo-radius); }
.bo-searchbox button, .bo-primary {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: var(--bo-control-h); min-height: 0; margin: 0; padding: 0 12px;
  border: 1px solid var(--bo-primary); border-radius: var(--bo-radius);
  background: var(--bo-primary); color: var(--bo-on-primary); cursor: pointer;
  font: inherit; font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 700;
}
.bo-searchbox button { width: var(--bo-control-h); padding: 0; border-radius: 0 var(--bo-radius) var(--bo-radius) 0; }
.bo-primary { padding: 0 12px; }
.bo-toolbar > .bo-primary { flex: none; width: auto; }
.bo-toolbar > .bo-primary:last-child { margin-left: auto; }
.bo-searchbox button:hover, .bo-primary:hover { background: var(--bo-primary-hover); border-color: var(--bo-primary-hover); }
.bo-searchbox button:active, .bo-primary:active { background: var(--bo-primary-pressed); border-color: var(--bo-primary-pressed); }
.bo-primary:disabled { opacity: .6; cursor: not-allowed; }
.bo-searchbox svg, .bo-primary svg, .bo-ghost svg, .bo-edit svg, .bo-x svg, .bo-chip svg,
.bo-outline svg, .bo-danger svg, .bo-create-btn svg, .bo-export svg, .bo-more svg {
  width: 16px; height: 16px; flex: none; fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
.bo-list { table-layout: fixed; }
.bo-list.bo-table th, .bo-list.bo-table td { border-left: 0; text-align: center; vertical-align: middle; overflow-wrap: anywhere; }
.bo-switch:disabled { opacity: .6; }
.bo-list th {
  text-transform: none; white-space: nowrap; padding: 6px;
  font-family: var(--bo-font); font-size: var(--bo-fs-th); font-weight: 700; color: var(--bo-text-head);
  border-bottom: 2px solid var(--bo-border-table);
}
.bo-list td { padding: 6px; font-family: var(--bo-font); font-size: var(--bo-fs-base); color: var(--bo-text); border-bottom: 1px solid var(--bo-border-row); }
.bo-list tbody tr:hover td { background: var(--bo-row-hover); }
.bo-list td form { margin: 0; }
.bo-groups.bo-table th:first-child, .bo-groups.bo-table td:first-child { text-align: left; }
/* Row action buttons: Kelola's kb-splitbutton-table — zinc fill, slate text, 24px. */
.bo-edit {
  width: auto; display: inline-flex; align-items: center; gap: 5px; height: var(--bo-control-h-sm); min-height: 0; margin: 0; padding: 0 8px;
  border: 1px solid var(--bo-btn-row-bg); border-radius: var(--bo-radius); background: var(--bo-btn-row-bg); color: var(--bo-btn-row-text);
  font: inherit; font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 700; cursor: pointer;
}
.bo-edit svg { width: 14px; height: 14px; }
.bo-edit:hover { background: var(--bo-btn-row-bg-hover); border-color: var(--bo-btn-row-bg-hover); color: var(--bo-btn-row-text); }
/* The blue membership chip (their blue-100/blue-600 pair, kept: not their primary). */
.bo-badge {
  display: inline-flex; align-items: center; height: 22px; padding: 0 6px;
  border-radius: var(--bo-radius-sm); background: var(--bo-chip-bg); color: var(--bo-chip-fg);
  font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 400;
}
/* Status tag: Kelola's p-tag — primary fill, white, 9px/700; .off is their grey. */
.bo-tag {
  display: inline-flex; align-items: center; padding: 2.5px 5px;
  border-radius: var(--bo-radius); background: var(--bo-primary); color: var(--bo-on-primary);
  font-family: var(--bo-font); font-size: var(--bo-fs-tag); font-weight: 700;
}
.bo-tag.off { background: var(--bo-btn-row-bg-hover); color: var(--bo-on-primary); }
.bo-tag.ok { background: var(--bo-success); color: var(--bo-on-primary); }
.bo-tag.info { background: var(--bo-info); color: var(--bo-on-primary); }
.bo-tag.warn { background: var(--bo-warning); color: var(--bo-on-primary); }
.bo-tag.bad { background: var(--bo-danger); color: var(--bo-on-primary); }
.bo-chip {
  display: inline-flex; align-items: center; gap: 5px; height: 22px; padding: 0 6px;
  border-radius: var(--bo-radius-sm); background: var(--bo-chip-bg); color: var(--bo-chip-fg);
  font-family: var(--bo-font); font-size: var(--bo-fs-base);
}
.bo-chip svg { width: 14px; height: 14px; }
/* Kelola's Aktif switch: a pill with the word inside, knob on the right when on. */
.bo-switch {
  position: relative; display: inline-flex; align-items: center; box-sizing: border-box; width: 52px; height: 26px; min-height: 0;
  margin: 0; padding: 0 0 0 8px; border: 0; border-radius: var(--bo-radius-pill); background: var(--bo-btn-row-bg-hover); color: var(--bo-on-primary);
  font: inherit; font-family: var(--bo-font); font-size: 11px; font-weight: 700; cursor: pointer; vertical-align: middle;
}
.bo-switch::after {
  content: ""; position: absolute; top: 4px; left: 4px; width: 18px; height: 18px; border-radius: 50%; background: var(--bo-surface); transition: left .15s;
}
.bo-switch:not(.on) { justify-content: flex-end; padding: 0 7px 0 0; }
.bo-switch.on { background: var(--bo-primary); }
.bo-switch.on::after { left: 30px; }
span.bo-switch { cursor: default; }
/* Kelola's modal (measured: 768 max width, 4.5px radius, dialog shadow, mask .4). */
.bo-dialog { width: min(768px, calc(100vw - 32px)); padding: 0; border: 0; border-radius: var(--bo-radius); box-shadow: var(--bo-shadow-dialog); font-family: var(--bo-font); }
/* A non-modal dialog (the gallery shows them inline) sits in flow; showModal()
   ones keep the UA's centered fixed positioning. */
dialog.bo-dialog:not(:modal) { position: static; margin: 12px 0; }
.bo-dialog::backdrop { background: var(--bo-mask); }
.bo-dialog form { margin: 0; }
.bo-dialog header, .bo-dialog footer { display: flex; align-items: center; gap: 12px; padding: var(--bo-dialog-pad); }
.bo-dialog header { justify-content: space-between; border-bottom: 1px solid var(--bo-border); }
.bo-dialog h2 { margin: 0; font-family: var(--bo-font); font-size: var(--bo-fs-dialog); font-weight: 600; color: var(--bo-text); }
.bo-x { display: grid; place-items: center; width: 24px; height: 24px; min-height: 0; margin: 0; padding: 0; border: 0; border-radius: 50%; background: none; color: var(--bo-text-muted); cursor: pointer; }
.bo-x:hover { background: var(--bo-surface-hover); }
.bo-x:focus-visible { box-shadow: var(--bo-focus); outline: 0; }
.bo-dialog-body { display: grid; grid-template-columns: 1fr 1fr; gap: 14px 16px; padding: var(--bo-dialog-pad); }
.bo-dialog-body label, .bo-dialog-body > div { display: grid; align-content: start; gap: 6px; font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 500; color: var(--bo-text-strong); }
.bo-dialog-body > [hidden] { display: none; }
.req > span:first-child:not(.bo-req)::after, .bo-lbl.req::after { content: " *"; color: var(--bo-required); }
.bo-dialog-body select, .bo-dialog-body input:not([type=radio]):not([type=checkbox]) {
  box-sizing: border-box; width: 100%; height: var(--bo-control-h); min-height: 0; margin: 0; padding: 0 9px;
  border: 2px solid var(--bo-border-input); border-radius: var(--bo-radius); background-color: var(--bo-surface);
  font: inherit; font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 400; color: var(--bo-text);
}
.bo-dialog-body select { background-color: var(--bo-field-bg); border-color: var(--bo-field-bg); }
.bo-dialog-body select:hover { background-color: var(--bo-field-bg-hover); border-color: var(--bo-field-bg-hover); }
.bo-dialog-body input:focus, .bo-dialog-body select:focus { border-color: var(--bo-primary); box-shadow: var(--bo-focus); outline: 0; }
.bo-dialog-body input[readonly], .bo-dialog-body select:disabled { background-color: var(--bo-surface-hover); color: var(--bo-text-muted); }
.bo-wide { grid-column: 1 / -1; }
.bo-dialog-body p.bo-wide { margin: 0; font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 400; }
.bo-code { display: flex; }
.bo-code i {
  display: grid; place-items: center; width: 40px; height: var(--bo-control-h);
  border: 2px solid var(--bo-border-input); border-width: 2px 0; font-style: normal; font-weight: 700;
}
.bo-seg { display: inline-flex; }
.bo-seg label { display: block; }
.bo-seg input { position: absolute; opacity: 0; pointer-events: none; }
.bo-seg span {
  display: grid; place-items: center; min-width: 80px; height: var(--bo-control-h); padding: 0 12px;
  border: 1px solid var(--bo-border-input); cursor: pointer;
  font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 600;
}
.bo-seg label + label span { border-left: 0; }
.bo-seg label:first-child span { border-radius: var(--bo-radius) 0 0 var(--bo-radius); }
.bo-seg label:last-child span { border-radius: 0 var(--bo-radius) var(--bo-radius) 0; }
.bo-seg input:checked + span { background: var(--bo-primary); border-color: var(--bo-primary); color: var(--bo-on-primary); }
.bo-seg input:disabled + span { cursor: not-allowed; color: var(--bo-text-muted); }
.bo-seg input:focus-visible + span { outline: 2px solid var(--bo-primary); outline-offset: 2px; }
.bo-dialog footer { justify-content: flex-end; border-top: 1px solid var(--bo-border); }
.bo-dialog footer button { flex: none; width: auto; }
.bo-dialog footer .bo-check { display: flex; gap: 6px; align-items: center; margin-right: auto; font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 400; }
.bo-dialog footer .bo-check input { width: auto; margin: 0; }
.bo-dialog footer .bo-check[hidden] { display: none; }
.bo-ghost {
  display: inline-flex; align-items: center; gap: 5px; height: var(--bo-control-h); min-height: 0; margin: 0; padding: 0 12px;
  border: 0; border-radius: var(--bo-radius); background: none; color: var(--bo-primary);
  font: inherit; font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 700; cursor: pointer;
}
.bo-ghost:hover { background: var(--bo-surface-hover); }

/* U2 remainder: secondary/danger/loading buttons, floating-label input,
   paginator, message variants, tree totals, activity cell, empty state. */
/* Row action split button (Kelola's Edit ▾): main + caret, joined radii. */
.bo-split { display: inline-flex; }
.bo-split .bo-edit { border-radius: var(--bo-radius) 0 0 var(--bo-radius); }
.bo-split summary.bo-edit { list-style: none; border-left: 0; border-radius: 0 var(--bo-radius) var(--bo-radius) 0; padding: 0 4px; }
.bo-split summary.bo-edit::-webkit-details-marker { display: none; }
.bo-toolbar > .bo-primary:last-child, .bo-toolbar > .bo-primary:has(+ .bo-more),
.bo-toolbar > .bo-primary:has(+ .bo-menu), .bo-toolbar > .bo-primary:has(+ .bo-more) { margin-left: auto; }
.bo-toolbar > .bo-more, .bo-toolbar > .bo-menu { margin-left: 8px; }
summary.bo-more { list-style: none; }
summary.bo-more::-webkit-details-marker { display: none; }
/* The three dots are zero-length strokes: 2px at 16px/24 left them a faint speck that screenshots lose, so they read as an empty box. */
summary.bo-more { color: var(--bo-text); }
summary.bo-more svg { stroke-width: 3.5; }
/* Sortable header affordance (Kelola's ↑↓). */
.th-sort { cursor: pointer; user-select: none; }
.th-sort::after { content: "↑↓"; margin-left: 3px; vertical-align: middle; font-size: 9px; letter-spacing: -1px; color: var(--bo-text-muted); }
.th-sort:hover::after, .th-sort[data-dir]::after { color: var(--bo-primary); }
/* Kind / currency chips (Kelola's Jasa/Produk/IDR marks; semantic greens/blues). */
.bo-badge.ok { background: var(--bo-success); color: var(--bo-on-primary); }
.bo-badge.warn { background: var(--bo-warning); color: var(--bo-on-primary); }
/* Account code chip (Saldo Awal / Trial Balance rows). */
.bo-codechip {
  display: inline-flex; align-items: center; height: 22px; padding: 0 6px;
  border-radius: var(--bo-radius-sm); background: var(--bo-chip-bg); color: var(--bo-chip-fg);
  font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 500;
}
/* Joined date-range pair, one Kelola-looking box. */
.bo-daterange { display: inline-flex; align-items: center; }
.bo-daterange input {
  box-sizing: border-box; height: var(--bo-control-h); min-height: 0; margin: 0; padding: 0 9px;
  border: 1px solid var(--bo-field-bg); background: var(--bo-field-bg); color: var(--bo-text);
  font: inherit; font-family: var(--bo-font); font-size: var(--bo-fs-base);
}
.bo-daterange input:first-of-type { border-radius: var(--bo-radius) 0 0 var(--bo-radius); }
.bo-daterange input:last-of-type { border-radius: 0 var(--bo-radius) var(--bo-radius) 0; }
.bo-daterange input + input { border-left: 0; }
/* Dasbor (U3): Kelola's widget page — hero card with the OU/periode filters and
   three metric cards. Page-only layout; the parts themselves are library parts. */
.bo-hero {
  border: 1px solid var(--bo-border); border-radius: var(--bo-radius); box-shadow: var(--bo-shadow-card);
  padding: var(--bo-content-pad); margin: 12px 0 0;
}
.bo-hero h2 {
  display: flex; align-items: center; gap: 8px; margin: 0 0 6px;
  font-family: var(--bo-font); font-size: var(--bo-fs-head); font-weight: 600; color: var(--bo-text-head);
}
.bo-hero h2 svg, .bo-widget h3 svg {
  width: 18px; height: 18px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
}
.bo-desc { margin: 0 0 4px; color: var(--bo-text-muted); font-family: var(--bo-font); font-size: var(--bo-fs-base); }
.bo-note {
  display: flex; align-items: center; gap: 6px; margin: 4px 0 12px;
  color: var(--bo-info); font-family: var(--bo-font); font-size: var(--bo-fs-base);
}
.bo-note svg { width: 14px; height: 14px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; }
.bo-filters { display: flex; align-items: end; justify-content: space-between; gap: 12px; margin: 12px 0 0; }
.bo-widgets { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; margin: 16px 0; }
.bo-widget {
  border: 1px solid var(--bo-border); border-radius: var(--bo-radius); box-shadow: var(--bo-shadow-card);
  padding: var(--bo-content-pad);
}
.bo-widget h3 {
  display: flex; align-items: center; gap: 8px; margin: 0 0 6px;
  font-family: var(--bo-font); font-size: 14px; font-weight: 600; color: var(--bo-text-head);
}
.bo-widget .nilai { display: block; margin: 10px 0 6px; font-family: var(--bo-font); font-size: 24px; font-weight: 700; color: var(--bo-text); }
.bo-widget .bo-delta { color: var(--bo-success); font-family: var(--bo-font); font-size: var(--bo-fs-base); }
.bo-widget .bo-delta.down { color: var(--bo-danger); }
.bo-widget .bo-delta.none { color: var(--bo-text-muted); }
.bo-widget.wide { grid-column: 1 / -1; }
.bo-widget .bo-empty { margin: 8px 0 0; color: var(--bo-text-muted); font-size: var(--bo-fs-base); }
.bo-widget .bo-scroll { overflow-x: auto; }
.bo-widget .bo-mini { margin: 8px 0 0; }
/* Dasbor charts: bar length is CSS (--v over --m), the template only prints the numbers. */
.bo-bars { display: flex; align-items: flex-end; gap: 3px; height: 150px; margin: 12px 0 0; padding: 0; list-style: none; }
.bo-bars li { flex: 1 1 0; min-width: 0; height: 100%; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; }
.bo-bars i { display: block; width: 100%; min-height: 1px; height: calc((100% - 16px) * var(--v) / var(--m)); background: var(--bo-primary); border-radius: var(--bo-radius-sm) var(--bo-radius-sm) 0 0; }
.bo-bars li:hover i { background: var(--bo-primary-hover); }
.bo-bars li.hot i { background: var(--bo-warning); }
.bo-bars small { height: 16px; line-height: 16px; color: var(--bo-text-muted); font-size: 9px; white-space: nowrap; }
.bo-hbars { margin: 10px 0 0; padding: 0; list-style: none; display: grid; gap: 6px; }
.bo-hbars li { display: grid; grid-template-columns: minmax(80px, 30%) 1fr auto; align-items: center; gap: 8px; font-size: var(--bo-fs-base); }
.bo-hbars span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bo-hbars em { display: block; height: 10px; background: var(--bo-field-bg); border-radius: var(--bo-radius-sm); }
.bo-hbars i { display: block; height: 100%; width: calc(var(--v) / var(--m) * 100%); background: var(--bo-primary); border-radius: var(--bo-radius-sm); }
.bo-hbars b { font-variant-numeric: tabular-nums; }
.bo-kv { margin: 6px 0 0; padding: 0; list-style: none; font-size: var(--bo-fs-base); }
.bo-kv li { display: flex; justify-content: space-between; gap: 12px; padding: 4px 0; border-top: 1px solid var(--bo-border-row); }
.bo-reviews { margin: 8px 0 0; padding: 0; list-style: none; display: grid; gap: 10px; font-size: var(--bo-fs-base); }
.bo-reviews li { padding-bottom: 8px; border-bottom: 1px solid var(--bo-border-row); }
.bo-reviews p { margin: 4px 0 0; }
.bo-reviews small { color: var(--bo-text-muted); margin-left: 6px; }
.bo-stars { color: var(--bo-warning); letter-spacing: 1px; margin-right: 6px; }
/* Segmen Akun: Kelompok Segmen list at the left of the items. */
.bo-seg-layout { display: grid; grid-template-columns: 240px minmax(0, 1fr); gap: 16px; align-items: start; margin-top: 12px; }
.bo-seg-groups { border: 1px solid var(--bo-border); border-radius: var(--bo-radius); padding: 12px; font-size: var(--bo-fs-base); }
.bo-seg-groups h3 { margin: 0 0 8px; font-size: 14px; font-weight: 600; color: var(--bo-text-head); }
.bo-seg-groups input[type=search] { width: 100%; box-sizing: border-box; margin-bottom: 8px; height: var(--bo-control-h); padding: 0 10px; font: inherit; font-size: var(--bo-fs-base); }
.bo-seg-groups ul { margin: 0; padding: 0; list-style: none; }
.bo-seg-groups a { display: flex; justify-content: space-between; gap: 8px; padding: 6px 8px; border-radius: var(--bo-radius-sm); color: var(--bo-text); text-decoration: none; }
.bo-seg-groups a:hover { background: var(--bo-row-hover); }
.bo-seg-groups a.on { background: var(--bo-primary-50); color: var(--bo-primary); font-weight: 600; }
.bo-seg-groups small { color: var(--bo-text-muted); }
@media (max-width: 720px) { .bo-seg-layout { grid-template-columns: 1fr; } }
/* Sub-tab icons (Kelola's Dasbor tabs carry icons; sized like the menu icons). */
.bo-subtabs svg {
  width: 14px; height: 14px; flex: none; margin-right: 6px; fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
}

/* Toolbar overflow (Kelola's ⋯). */
.bo-more {
  display: grid; place-items: center; width: var(--bo-control-h); height: var(--bo-control-h); min-height: 0; margin: 0; padding: 0;
  border: 1px solid var(--bo-border); border-radius: var(--bo-radius); background: var(--bo-surface); color: var(--bo-text-muted);
  cursor: pointer;
}
.bo-outline {
  display: inline-flex; align-items: center; gap: 6px; height: var(--bo-control-h); min-height: 0; margin: 0; padding: 0 12px;
  border: 1px solid var(--bo-border-input); border-radius: var(--bo-radius); background: var(--bo-surface); color: var(--bo-text);
  font: inherit; font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 700; cursor: pointer;
}
.bo-outline:hover { border-color: var(--bo-primary); color: var(--bo-primary); }
.bo-danger {
  display: inline-flex; align-items: center; gap: 6px; height: var(--bo-control-h); min-height: 0; margin: 0; padding: 0 12px;
  border: 1px solid var(--bo-danger); border-radius: var(--bo-radius); background: var(--bo-danger); color: var(--bo-on-primary);
  font: inherit; font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 700; cursor: pointer;
}
.bo-danger:hover { filter: brightness(.92); }
.bo-spin {
  width: 14px; height: 14px; flex: none; border: 2px solid currentColor; border-top-color: transparent; border-radius: 50%;
  animation: bo-spin .8s linear infinite;
}
@keyframes bo-spin { to { transform: rotate(360deg); } }
/* Floating label on the border (Kelola's filled controls); empty label sits in
   the field and floats up on focus or fill. */
.bo-float { position: relative; display: block; }
.bo-float input {
  box-sizing: border-box; width: 100%; height: var(--bo-control-h); padding: 0 9px;
  border: 2px solid var(--bo-border-input); border-radius: var(--bo-radius); background: var(--bo-surface);
  font: inherit; font-family: var(--bo-font); font-size: var(--bo-fs-base); color: var(--bo-text);
}
.bo-float input:focus { border-color: var(--bo-primary); box-shadow: var(--bo-focus); outline: 0; }
.bo-float > span {
  position: absolute; top: 50%; left: 9px; transform: translateY(-50%); padding: 0; background: none;
  color: var(--bo-text-muted); pointer-events: none;
  font-family: var(--bo-font); font-size: var(--bo-fs-base); line-height: 14px; transition: top .12s, font-size .12s;
}
.bo-float input:focus + span, .bo-float input:not(:placeholder-shown) + span {
  top: -8px; left: 8px; padding: 0 4px; background: var(--bo-surface); font-size: 10px; color: var(--bo-primary);
}
.bo-float.err input { border-color: var(--bo-danger); }
.bo-float.err > span { color: var(--bo-danger); }
.bo-err { display: block; margin-top: 4px; color: var(--bo-danger); font-family: var(--bo-font); font-size: var(--bo-fs-base); }
.bo-hint { display: block; margin-top: 4px; color: var(--bo-text-muted); font-family: var(--bo-font); font-size: var(--bo-fs-base); }
.bo-hint.bo-sub { margin: 0; }
.bo-hint.bo-click { cursor: pointer; }
.bo-left { text-align: left; }
.bo-primary.off { opacity: .55; cursor: default; }
.bo-flash.info { background: var(--bo-msg-info-bg); color: var(--bo-msg-info); border-left-color: var(--bo-msg-info); }
.bo-flash.warn { background: var(--bo-msg-warn-bg); color: var(--bo-msg-warn); border-left-color: var(--bo-msg-warn); }
.bo-empty { padding: 32px; text-align: center; color: var(--bo-text-muted); font-family: var(--bo-font); font-size: var(--bo-fs-base); }
.bo-activity { color: var(--bo-text-muted); line-height: 1.4; text-align: left; }
.bo-activity b { display: block; font-weight: 500; color: var(--bo-text); }
.bo-total td { font-weight: 700; border-top: 2px solid var(--bo-border-table); border-bottom: 0; }
/* Kelola's p-paginator: "Showing 1 to 3 of 3", page pills, rows-per-page. */
.bo-pager {
  display: flex; align-items: center; gap: 4px; min-height: 54px; padding: 9px 18px;
  background: var(--bo-surface); border-radius: var(--bo-radius);
  font-family: var(--bo-font); font-size: var(--bo-fs-base); color: var(--bo-text-muted);
}
.bo-pager .count { margin-right: auto; white-space: nowrap; }
.bo-pager button, .bo-pager a {
  display: grid; place-items: center; min-width: 34px; width: auto; height: 34px; min-height: 0; padding: 0 6px; margin: 0;
  border: 1px solid var(--bo-surface); border-radius: var(--bo-radius-sm); background: none; color: var(--bo-text);
  font: inherit; font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 500; cursor: pointer; text-decoration: none;
}
.bo-pager button:hover, .bo-pager a:hover { background: var(--bo-row-hover); }
.bo-pager button:disabled { opacity: .4; cursor: default; background: none; }
.bo-pager .on { background: var(--bo-primary); color: var(--bo-on-primary); }
.bo-pager select {
  height: var(--bo-control-h); margin-left: 8px; padding: 0 9px;
  align-items: center; /* a base-select <select> is a flex box that stacks its text from the top */
  border: 1px solid var(--bo-field-bg); border-radius: var(--bo-radius); background: var(--bo-field-bg);
  font: inherit; font-family: var(--bo-font); font-size: var(--bo-fs-base); color: var(--bo-text);
}

/* Kelola's filter dropdown (js/bo-multi.js): grey box with the field name; once
   something is picked, the name floats on a primary-outlined border. */
/* Before bo-multi.js runs (the page paints first, and a view transition holds
   that frame), the raw list shows as the same empty grey box: no flash of listboxes. */
.js select[data-multi] {
  flex: 1 1 150px; max-width: 280px; min-width: 0; box-sizing: border-box; height: var(--bo-control-h); min-height: 0; margin: 0; padding: 0;
  overflow: hidden; border: 1px solid var(--bo-field-bg); border-radius: var(--bo-radius); background: var(--bo-field-bg); color: transparent; pointer-events: none;
}
.js select[data-multi] option { visibility: hidden; }
.bo-multi { position: relative; flex: 1 1 150px; max-width: 280px; min-width: 0; }
.bo-multi-btn {
  position: relative; display: flex; align-items: center; gap: 8px; box-sizing: border-box; width: 100%; height: var(--bo-control-h);
  min-height: 0; margin: 0; padding: 0 8px 0 9px; border: 1px solid var(--bo-field-bg); border-radius: var(--bo-radius); background: var(--bo-field-bg);
  color: var(--bo-text); font: inherit; font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 400; text-align: left; cursor: pointer;
}
.bo-multi-btn:hover { background: var(--bo-field-bg-hover); border-color: var(--bo-field-bg-hover); }
.bo-multi-label { flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--bo-text-muted); }
.bo-multi-value { display: none; flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.bo-multi-chev svg { display: block; width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; }
.bo-multi.has-value .bo-multi-btn, .bo-multi.open .bo-multi-btn { background: var(--bo-surface); border-color: var(--bo-primary); }
.bo-multi.has-value .bo-multi-label {
  position: absolute; top: -8px; left: 8px; flex: none; padding: 0 4px; background: var(--bo-surface);
  font-family: var(--bo-font); font-size: 10px; line-height: 14px; color: var(--bo-primary);
}
.bo-multi.has-value .bo-multi-value { display: block; }
.bo-multi.open .bo-multi-chev svg { transform: rotate(180deg); }
.bo-multi-pop {
  position: absolute; top: calc(100% + 4px); left: 0; z-index: 30; box-sizing: border-box; width: max(100%, 320px);
  background: var(--bo-surface); border-radius: var(--bo-radius); box-shadow: var(--bo-shadow-2);
}
.bo-multi-head { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-bottom: 1px solid var(--bo-border-row); }
.bo-multi-find { flex: 1; display: flex; align-items: center; min-width: 0; height: 30px; padding: 0 8px; border: 1px solid var(--bo-border-input); border-radius: var(--bo-radius); }
.bo-multi-find input {
  flex: 1; min-width: 0; height: 100%; min-height: 0; margin: 0; padding: 0; border: 0; outline: 0; box-shadow: none;
  background: none; font: inherit; font-family: var(--bo-font); font-size: var(--bo-fs-base);
}
.bo-multi-find svg, .bo-multi-clear svg { width: 16px; height: 16px; flex: none; fill: none; stroke: var(--bo-text-muted); stroke-width: 2; }
.bo-multi-clear { display: grid; place-items: center; width: 24px; height: 24px; min-height: 0; margin: 0; padding: 0; border: 0; background: none; cursor: pointer; }
.bo-multi-list { max-height: 250px; overflow-y: auto; margin: 0; padding: 4px 0; list-style: none; }
.bo-multi-list label {
  display: flex; align-items: center; gap: 10px; padding: 8px 12px;
  font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 400; cursor: pointer;
}
.bo-multi-list label:hover { background: var(--bo-surface-hover); }
.bo-multi input:is([type=checkbox], [type=radio]) { width: 16px; height: 16px; min-height: 0; margin: 0; flex: none; accent-color: var(--bo-primary); }

/* Phone: the tab row scrolls sideways, the section menu becomes a row of chips. */
@media (max-width: 860px) {
  .bo-top { flex-wrap: wrap; height: auto; padding: 8px 16px 0; }
  .bo-brand { width: auto; justify-content: flex-start; }
  .bo-brand img { height: 26px; }
  .bo-create-btn { margin-left: auto; }
  .bo-tools { order: 2; flex-basis: 100%; padding: 8px 0 0; gap: 6px; }
  .bo-search { flex: 1; width: auto; }
  .bo-search kbd { display: none; }
  .bo-tabs { order: 3; flex-basis: 100%; height: 48px; margin: 0 -16px; padding: 0 4px; }
  .bo-shell { display: block; }
  .bo-side {
    position: static; flex-direction: row; overflow-x: auto; min-height: 0;
    padding: 0 16px; border-right: 0; border-bottom: 1px solid var(--bo-border);
  }
  .bo-side h2 { display: none; }
  .bo-sidetoggle { display: none; }
  .bo-side-off .bo-side { display: flex; }
  .bo-side a, .bo-side .off { white-space: nowrap; }
  .bo-side a.on { background: none; }
  .bo-shell > main { padding: 0 0 32px; }
}

/* Buat Transaksi: Kelola keeps the label above the field, so the combos drop their floating name. */
.bo-dialog .bo-multi { max-width: none; }
.bo-dialog .bo-multi.has-value .bo-multi-label { display: none; }
.bo-dialog .bo-multi.has-value .bo-multi-btn { background: var(--bo-field-bg); border-color: var(--bo-field-bg); }
.bo-dialog .bo-multi.open .bo-multi-btn { border-color: var(--bo-primary); }
.bo-dialog .bo-multi-pop { width: 100%; }
.bo-multi-list li.off label { color: var(--bo-text-muted); cursor: default; }
.bo-multi-list li.off label:hover { background: none; }
.bo-multi-list li.off input { display: none; }
.bo-multi-list .bo-chip, .bo-multi-value .bo-chip {
  flex: none; padding: 2px 6px; border-radius: var(--bo-radius); background: var(--bo-primary-100); color: var(--bo-primary);
  font-size: var(--bo-fs-tag); font-weight: 700;
}
.bo-dialog footer .bo-primary:disabled { opacity: .55; cursor: not-allowed; }

/* Lihat Transaksi: Kelola's two filter rows, status chips (draft grey, approved green, void
   dark grey), the light "Butuh Tindakan" chip, and the "Tampilkan Semua" fold. */
.bo-txn-filter .bo-break { flex-basis: 100%; height: 0; }
.bo-txn-filter select, .bo-txn-filter input[type=date] { flex: 1 1 150px; max-width: none; }
.bo-txn-filter .bo-searchbox { flex: 1 1 260px; }
.bo-txn-filter .bo-until { font-family: var(--bo-font); font-size: var(--bo-fs-base); color: var(--bo-text); padding: 0 6px; }
.bo-txn-filter .bo-outline, .bo-txn-filter .bo-export { flex: none; }
.bo-txn-filter .bo-export { margin-left: auto; }
.bo-txn th, .bo-txn td { vertical-align: top; }
.bo-txn td { padding-top: 10px; padding-bottom: 10px; }
.bo-txn th:nth-child(-n+2), .bo-txn td:nth-child(-n+2) { text-align: left; }
.bo-txn th.bo-mid, .bo-txn td.bo-mid, .bo-txn td.bo-activity { text-align: center; }
.bo-tag.bo-status, .bo-tag.need {
  display: inline-flex; justify-content: center; box-sizing: border-box; min-width: 96px; max-width: 100%;
  padding: 5px 10px; font-size: var(--bo-fs-base); font-weight: 700;
}
.bo-tag.bo-status.off { background: var(--bo-border); color: var(--bo-text-head); }
.bo-tag.bo-status.ok { background: var(--bo-success-soft); color: var(--bo-success-text); }
.bo-tag.bo-status.void { background: var(--bo-text-muted); color: var(--bo-on-primary); }
.bo-tag.need { min-width: 110px; padding: 8px 12px; background: var(--bo-primary-100); color: var(--bo-primary); }
.bo-expand summary { cursor: pointer; list-style: none; color: var(--bo-primary); }
.bo-expand summary::-webkit-details-marker { display: none; }
.bo-expand p { margin: 4px 0 0; }
.bo-outline.off { opacity: .55; cursor: default; }
.bo-pager a[aria-disabled=true] { opacity: .4; pointer-events: none; }
.bo-shell > main .bo-pager a.on { color: var(--bo-on-primary); }
.bo-multi-list li.ph { display: none; }
/* The dialog's field rules (grid labels, bordered inputs) must not reach into the combo popup. */
.bo-dialog-body .bo-multi-pop label, .bo-dialog-body .bo-multi-list label { display: flex; gap: 10px; align-items: center; font-weight: 400; color: var(--bo-text); }
.bo-dialog-body .bo-multi-find { display: flex; gap: 6px; }
.bo-dialog-body .bo-multi-find input:not([type=radio]):not([type=checkbox]) { flex: 1; height: 100%; padding: 0; border: 0; border-radius: 0; background: none; }
.bo-dialog-body .bo-multi-find input:focus { box-shadow: none; }

/* One document (transaksi-detail.html): left lines, right rail with the status button. */
.bo-doc-grid { display: grid; grid-template-columns: minmax(0, 1fr) 360px; gap: 0 28px; align-items: start; }
.bo-doc-main { padding-top: 0; }
.bo-doc-main .bo-crumb { margin-bottom: 8px; }
.bo-doc-main .bo-lbl { margin: 14px 0 6px; font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 600; color: var(--bo-text-strong); }
.bo-doc-ou, .bo-doc-remark { margin: 0 0 18px; font-family: var(--bo-font); font-size: var(--bo-fs-base); color: var(--bo-text-muted); }
.bo-doc-total { margin: 26px 0 8px; text-align: right; font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 600; color: var(--bo-text-head); }
.bo-lines td { vertical-align: top; padding-top: 12px; padding-bottom: 12px; }
.bo-lines th, .bo-lines td { text-align: left; }
.bo-lines th.bo-mid, .bo-lines td.bo-mid { text-align: center; }
.bo-lines th.num, .bo-lines td.num { text-align: right; }
.bo-line-act { display: flex; align-items: center; gap: 10px; color: var(--bo-text-muted); }
.bo-line-act svg, .bo-trash svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.8; }
.bo-trash { display: grid; place-items: center; width: 24px; min-height: 0; height: 24px; margin: 0; padding: 0; border: 0; background: none; color: var(--bo-text-muted); cursor: pointer; }
.bo-trash:disabled { opacity: .45; cursor: default; }
.bo-trash:not(:disabled):hover { color: var(--bo-danger); }
.bo-chip.bo-acc { background: var(--bo-primary-100); color: var(--bo-primary); font-weight: 700; margin-right: 6px; }
.bo-line-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.bo-sifat { display: inline-block; min-width: 68px; padding: 5px 12px; border-radius: var(--bo-radius); text-align: center; font-weight: 700; }
.bo-sifat.d { background: #dbeafe; color: #1d4ed8; }
.bo-sifat.c { background: #fee2e2; color: #dc2626; }
.bo-doc-rail { position: sticky; top: 8px; padding: 6px 0 24px 20px; border-left: 1px solid var(--bo-border); font-family: var(--bo-font); font-size: var(--bo-fs-base); }
.bo-doc-rail-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.bo-status-btn { list-style: none; display: inline-flex; align-items: center; gap: 8px; height: var(--bo-control-h); padding: 0 12px; border-radius: var(--bo-radius); color: var(--bo-on-primary); font-weight: 700; cursor: pointer; }
.bo-status-btn::-webkit-details-marker { display: none; }
.bo-status-btn svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2.4; }
.bo-status-btn.ok { background: var(--bo-success-text); }
.bo-status-btn.void { background: var(--bo-text-strong); }
.bo-status-btn.draft { background: var(--bo-text-muted); }
.bo-status-menu .bo-pop { left: 0; right: auto; min-width: 180px; }
.bo-status-menu .bo-pop button { width: 100%; text-align: left; }
.bo-status-menu .bo-pop button.bad { color: var(--bo-danger); }
.bo-doc-icons { display: inline-flex; align-items: center; gap: 4px; }
.bo-doc-icons .bo-icon { display: grid; place-items: center; width: 34px; height: 34px; min-height: 0; margin: 0; padding: 0; border: 0; background: none; color: var(--bo-text-muted); }
.bo-doc-icons button.bo-icon:hover { background: var(--bo-surface-hover); }
.bo-doc-icons .bo-icon svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; }
.bo-doc-icons .bo-icon.off { opacity: .45; cursor: default; }
.bo-doc-no { margin: 14px 0 0; padding: 8px 0; font-family: var(--bo-font); font-size: var(--bo-fs-head); font-weight: 600; color: var(--bo-text-head); }
.bo-doc-no span:first-child { font-weight: 300; margin-right: 4px; }
.bo-doc-fields { margin: 4px 0 0; }
.bo-doc-fields dt { margin: 18px 0 6px; font-weight: 600; color: var(--bo-text-strong); }
.bo-doc-fields dd { margin: 0; padding: 0 0 0 1px; color: var(--bo-text-muted); }
.bo-chip.bo-cur { background: var(--bo-success-soft); color: var(--bo-success-text); }
.bo-denied { display: grid; justify-items: center; gap: 12px; padding: 80px 0; text-align: center; }
.bo-denied svg { width: 72px; height: 72px; fill: none; stroke: var(--bo-warning); stroke-width: 1.4; }
.bo-denied h2 { margin: 0; font-family: var(--bo-font); font-size: var(--bo-fs-title); font-weight: 600; color: var(--bo-text-head); }
@media (max-width: 1100px) {
  .bo-doc-grid { grid-template-columns: 1fr; }
  .bo-doc-rail { position: static; padding-left: 0; border-left: 0; border-top: 1px solid var(--bo-border); }
}
@media print { .bo-top, .bo-crumb, .bo-doc-icons, .bo-status-menu .bo-pop { display: none; } .bo-doc-grid { grid-template-columns: 1fr 300px; } }
.bo-lines input[type=text] { box-sizing: border-box; width: 100%; height: var(--bo-control-h); min-height: 0; margin: 0; padding: 0 9px;
  border: 2px solid var(--bo-border-input); border-radius: var(--bo-radius); background: var(--bo-surface); font: inherit; font-family: var(--bo-font); font-size: var(--bo-fs-base); color: var(--bo-text); }
.bo-lines input[type=text]:focus { border-color: var(--bo-primary); box-shadow: var(--bo-focus); outline: 0; }
.bo-lines input.num { text-align: right; }
.bo-lines .bo-multi { max-width: none; }
.bo-lines .bo-seg { display: inline-flex; }
.bo-lines .bo-multi-pop { width: max(100%, 320px); }
.bo-lines .bo-multi.has-value .bo-multi-label { display: none; }
.bo-lines .bo-seg span { padding-left: 8px; padding-right: 8px; }

/* Entry pages (Pembelian, Penjualan, Kas Bank, Persediaan): Kelola's flow diagram. A grey
   canvas, a 12-column grid, 3-column cards with a blue chevron under the ones that lead on. */
.bo-flow { margin-top: 24px; padding: 24px; background: var(--bo-ground); }
.bo-flow-grid { display: grid; grid-template-columns: repeat(12, 1fr); }
.bo-flow-cell { padding: 0 24px; min-width: 0; }
.bo-flow.short .bo-flow-cell:nth-child(n+2) { margin-top: 12px; }
.bo-flow-card { padding: 12px; background: var(--bo-surface); border-radius: var(--bo-radius); box-shadow: var(--bo-shadow-2); }
.bo-flow-card h2 {
  display: flex; align-items: center; justify-content: center; margin: 0; border-block: 1px solid var(--bo-border);
  font-family: var(--bo-font); font-size: 15.6px; font-weight: 500; color: var(--bo-text-head);
}
.bo-flow-card h2 span { margin: 3px 0; }
.bo-flow-card p { min-height: 48px; margin: 12px 0; font-size: var(--bo-fs-base); text-align: center; color: var(--bo-text-faint); }
.bo-flow-card .bo-outline {
  display: flex; height: 36px; margin: 0 auto; border: .8px solid var(--bo-primary); color: var(--bo-primary);
}
.bo-flow-card .bo-outline:hover { background: var(--bo-primary-50); }
.bo-flow-card .bo-outline:disabled { opacity: .55; cursor: not-allowed; background: var(--bo-surface); }
.bo-flow-arrow { display: block; width: 20px; height: 33px; margin: 0 auto; padding: 6px 0; box-sizing: border-box; fill: none; stroke: var(--bo-info); stroke-width: 2; }
.bo-flow-cell:not(:has(.bo-flow-arrow)) { padding-bottom: 33px; }

/* Paper statements (Laba Rugi, Neraca): Kelola's white sheet on a grey canvas. Left edges follow
   Kelola's measured ones (section 36px in, accounts 78px in, 18px per nesting level). */
.bo-sheet-canvas { display: flex; justify-content: center; margin-top: 12px; padding: 60px; background: var(--bo-ground); }
.bo-sheet { width: 100%; max-width: 1027px; box-sizing: border-box; padding: 36px 0; background: var(--bo-surface); border-radius: var(--bo-radius); box-shadow: var(--bo-shadow-2); font-family: var(--bo-font); font-size: 15px; color: var(--bo-text); }
.bo-sheet h2 { display: flex; flex-direction: column; margin: 0 0 18px; font-size: 24px; font-weight: 700; line-height: 30px; text-align: center; color: var(--bo-text-strong); }
.bo-sheet-line { display: flex; justify-content: space-between; gap: 24px; min-height: 23px; padding: 0 36px 0 calc(36px + 18px * var(--lv, 0)); }
.bo-sheet-line.row { padding-left: calc(78px + 18px * (var(--lv, 1) - 1)); }
.bo-sheet-line.row:hover { background: var(--bo-border); }
.bo-sheet-line.head, .bo-sheet-line.total { font-weight: 500; color: var(--bo-text-strong); }
.bo-sheet-line.total { margin: 0 36px 0 calc(36px + 18px * var(--lv, 0)); padding: 0; border-top: 2px solid var(--bo-text-strong); }
.bo-sheet-line b { font-weight: inherit; white-space: nowrap; }
.bo-sheet-line.l1 { --lv: 1; } .bo-sheet-line.l2 { --lv: 2; } .bo-sheet-line.l3 { --lv: 3; }
.bo-report .bo-opening td { font-weight: 600; background: var(--bo-surface-hover); }

/* Print: just the report, A4 landscape, whatever the width of the screen. */
@page report { size: A4 landscape; margin: 12mm; }
@media print {
  .bo-report-print { page: report; }
  .bo-top, .bo-side, .bo-sidetoggle, .bo-crumb, .bo-subtabs, .bo-report-filter, .bo-pager { display: none !important; }
  .bo-shell > main { padding: 0; }
  .bo-sheet-canvas { padding: 0; background: none; }
  .bo-sheet { max-width: none; box-shadow: none; }
  .bo-report th, .bo-report td { white-space: normal; }
}

/* Master lists: the phone field is a country-code select next to the number. */
.bo-phone { display: grid; grid-template-columns: 150px 1fr; gap: 6px; }

/* Master edit page (Ubah Pemasok): a white card of accordion sections on the grey canvas. */
.bo-edit-canvas { margin-top: 12px; padding: 60px 36px; background: var(--bo-ground); }
.bo-edit-card { max-width: 1212px; margin: 0 auto; background: var(--bo-surface); border-radius: var(--bo-radius); box-shadow: var(--bo-shadow-card); padding: 18px; }
.bo-edit-card details { border: 1px solid var(--bo-border); border-bottom-width: 0; }
.bo-edit-card details:last-of-type { border-bottom-width: 1px; }
.bo-edit-card summary { padding: 12px 16px; font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 700; color: var(--bo-text-strong); background: var(--bo-ground); cursor: pointer; }
.bo-edit-card .bo-dialog-body { grid-template-columns: 1fr 1fr 1fr; padding: 16px; }
.bo-edit-card .bo-dialog-body .bo-wide { grid-column: 1 / -1; }
.bo-edit-foot { display: flex; align-items: center; gap: 12px; margin-top: 18px; }
.bo-stars { display: inline-flex; gap: 6px; height: var(--bo-control-h); align-items: center; }
.bo-stars svg { width: 18px; height: 18px; fill: none; stroke: var(--bo-primary); stroke-width: 1.6; }
.bo-stars svg.on { fill: var(--bo-primary); }
.bo-role { display: flex; justify-content: space-between; align-items: center; gap: 12px; }

/* Akses Halaman: Kelola's checklist tree (feature groups, each item with its permission tiers). */
.bo-pa-bar { display: flex; align-items: center; gap: 18px; margin: 12px 0; }
.bo-link { display: inline-flex; align-items: center; gap: 6px; padding: 0; border: 0; background: none; font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 700; color: var(--bo-text); cursor: pointer; list-style: none; }
.bo-link:hover { color: var(--bo-primary); }
.bo-link svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; }
.bo-pa-tree { padding: 16px; border: 1px solid var(--bo-border); border-radius: var(--bo-radius); background: var(--bo-surface); }
.bo-pa-filter { position: relative; display: block; margin-bottom: 12px; }
.bo-pa-filter input { width: 100%; box-sizing: border-box; height: var(--bo-control-h); padding: 0 36px 0 12px; border: 2px solid var(--bo-border-input); border-radius: var(--bo-radius); font: inherit; font-size: var(--bo-fs-base); }
.bo-pa-filter svg { position: absolute; right: 12px; top: 9px; width: 16px; height: 16px; fill: none; stroke: var(--bo-text-muted); stroke-width: 2; }
.bo-pa-group > summary { padding: 8px 0; font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 700; color: var(--bo-text-strong); cursor: pointer; }
.bo-pa-item { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 6px 0 6px 24px; border-top: 1px solid var(--bo-border-row); font-family: var(--bo-font); font-size: var(--bo-fs-base); }
.bo-pa-item[hidden], .bo-pa-group[hidden] { display: none; }

/* Report grids (Analisa Vertikal / Horizontal): Kelola's centred title above the table; the "Total ..." line of a list. */
.bo-list-title { display: flex; flex-direction: column; margin: 18px 0; font-size: 20px; font-weight: 700; line-height: 26px; text-align: center; color: var(--bo-text-strong); }
.bo-list-total { margin: 0 0 8px; text-align: right; font-size: 16px; font-weight: 600; color: var(--bo-text-strong); }

/* Full-page forms (Kelola POS › Tambah): breadcrumb line with Kembali / Simpan at its right, fields in two columns. */
.bo-form-top { display: flex; align-items: center; gap: 16px; }
.bo-form-top .bo-crumb { flex: 1; }
.bo-form-page { margin-top: 12px; }
.bo-form-page .bo-dialog-body { padding-left: 0; padding-right: 0; }
.bo-stepper { display: flex; gap: 16px; margin: 0; padding: 12px 0; list-style: none; font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 600; color: var(--bo-text-muted); }
.bo-stepper li { display: flex; align-items: center; gap: 8px; }
.bo-stepper li.on { color: var(--bo-text-strong); }
.bo-stepper b { display: inline-grid; place-items: center; width: 22px; height: 22px; border-radius: 50%; background: var(--bo-border); font-size: 12px; }
.bo-stepper li.on b { background: var(--bo-primary); color: #fff; }
.bo-radio { display: flex !important; flex-direction: row; gap: 8px; align-items: center; font-weight: 400 !important; }
.bo-form-table-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
.bo-trash { width: 18px; height: 18px; fill: none; stroke: var(--bo-text-muted); stroke-width: 1.6; vertical-align: middle; }
.bo-rule { margin: 4px 0; font-family: var(--bo-font); font-size: 12px; font-weight: 600; color: var(--bo-text-link, var(--bo-primary)); }
.bo-rule.warn { color: var(--bo-warning, #c25b00); }
.bo-drop {
  position: relative; display: grid; justify-items: center; gap: 6px; margin-top: 16px; padding: 24px;
  border: 1.6px dashed var(--bo-border-input); border-radius: var(--bo-radius); background: var(--bo-ground);
  font-family: var(--bo-font); font-size: var(--bo-fs-base); text-align: center; cursor: pointer;
}
.bo-drop.over { border-color: var(--bo-primary); background: var(--bo-primary-50); }
.bo-drop-input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.bo-drop:focus-within { box-shadow: var(--bo-focus); }
.bo-drop-icon { width: 30px; height: 30px; fill: none; stroke: var(--bo-text-muted); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.bo-drop strong { font-size: 13.5px; font-weight: 700; color: var(--bo-text-strong); }
.bo-drop-hint { color: var(--bo-text-muted); }
.bo-drop .bo-drop-btn { width: min(326px, 100%); justify-content: center; margin-top: 6px; }
.bo-drop-file { font-weight: 600; color: var(--bo-text); }
.bo-drop-file:empty { display: none; }
.bo-upload .bo-dialog-body { display: block; }
.bo-upload h3 { margin: 0 0 8px; font-family: var(--bo-font); font-size: 16px; font-weight: 600; }

/* Kelola POS › Metode Pembayaran: a method list at the left, its detail at the right. */
.bo-panes { display: grid; grid-template-columns: 303px 1fr; gap: 16px; margin-top: 12px; align-items: start; }
.bo-pane-head { margin: 0 0 12px; padding: 12px; background: var(--bo-border); font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 700; text-align: center; color: var(--bo-text-strong); }
.bo-pane-hint { text-align: center; padding: 24px 0; }
.bo-method-list { margin: 0; padding: 0; list-style: none; }
.bo-method-list a { display: flex; align-items: center; gap: 12px; padding: 12px; color: var(--bo-text-strong); font-weight: 600; text-decoration: none; }
.bo-method-list li.on a, .bo-method-list a:hover { background: var(--bo-surface-hover); }
.bo-method-list b { display: inline-grid; place-items: center; width: 28px; height: 28px; border-radius: 50%; background: var(--bo-primary); color: var(--bo-on-primary); font-size: 13px; }

/* Ubah Pelanggan's Profil block: a centred avatar, the upload button and the referral code. */
.bo-profile { display: grid; justify-items: center; gap: 12px; max-width: 1212px; margin: 0 auto 18px; }
.bo-profile h2 { margin: 0; font-family: var(--bo-font); font-size: 18px; font-weight: 600; }
.bo-profile p { margin: 0; font-family: var(--bo-font); font-size: var(--bo-fs-base); }
.bo-profile-avatar { width: 120px; height: 120px; border-radius: 50%; background: var(--bo-info); }

/* Ubah Gudang: one field per row, label at the left. */
.bo-inline-form { display: grid; gap: 14px; margin-top: 18px; padding: 0 18px; }
.bo-inline-form label { display: grid; grid-template-columns: 200px minmax(0, 1fr); align-items: center; gap: 16px; font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 600; color: var(--bo-text-strong); }
.bo-inline-form input:not([type=radio]):not([type=checkbox]), .bo-inline-form select { height: var(--bo-control-h); box-sizing: border-box; padding: 0 12px; border: 2px solid var(--bo-border-input); border-radius: var(--bo-radius); font: inherit; font-weight: 400; background: var(--bo-surface); }
.bo-inline-form input[readonly], .bo-inline-form select:disabled { background: var(--bo-surface-hover); color: var(--bo-text-muted); }
.bo-ou-chip { display: inline-flex; gap: 8px; align-items: center; font-weight: 400; color: var(--bo-text-muted); }

/* Akses Halaman tree: area > group > one checkbox per sentence. */
.bo-pa-area > summary, .bo-pa-group > summary { display: flex; align-items: center; gap: 10px; padding: 8px 0; cursor: pointer; font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 700; color: var(--bo-text-strong); }
.bo-pa-group { margin-left: 24px; }
.bo-pa-area[hidden], .bo-pa-group[hidden] { display: none; }
.bo-pa-item { display: flex; align-items: center; justify-content: flex-start; gap: 10px; padding: 6px 0 6px 28px; border-top: 0; font-weight: 400; cursor: pointer; }
.bo-pa-area input[type=checkbox], .bo-pa-item input[type=checkbox] { width: 16px; height: 16px; margin: 0; accent-color: var(--bo-primary); }

/* 2026-10-12 side-by-side fixes against Kelola at 1920 (measured off var/ui-ref/kelola-*.png). */
/* Page padding: Kelola's content starts 24px from the menu rule; Dasbor, with no menu, 36px. */
.bo-shell > main { padding-left: 24px; padding-right: 24px; }
.bo-shell > main.bo-dash { padding-left: 36px; padding-right: 36px; }
/* Left menu: the open page is a 2px primary bar and primary text, no fill; a primary-tint rule closes the column. */
.bo-side { border-right: 1px solid var(--bo-primary-100); }
.bo-side a.on { background: none; border-radius: 0; box-shadow: inset 2px 0 0 var(--bo-primary); }
/* Debit/Kredit mark in the Akun list: Kelola's blue tag, 33px high, bold. */
.bo-badge.dc { height: 33px; padding: 0 18px; justify-content: center; min-width: 84px; box-sizing: border-box; font-size: 14px; font-weight: 700; }
/* Dasbor: 21px card padding, floating filter labels, the OU type chip, "?" help icons, smaller note lines. */
.bo-hero, .bo-widget { padding: 21px; }
.bo-hero > .bo-note { font-size: 10px; }
.bo-hero .bo-filters { margin-top: 22px; }
.bo-widget .bo-note { font-size: 9px; }
.bo-widget .nilai { font-size: 20px; }
.bo-help { width: 16px; height: 16px; flex: none; fill: none; stroke: var(--bo-text-muted); stroke-width: 1.6; stroke-linecap: round; cursor: help; }
.bo-field.bo-flabel { position: relative; }
.bo-field.bo-flabel > span:first-child {
  position: absolute; top: -8px; left: 10px; z-index: 1; padding: 0 2px; background: var(--bo-surface);
  font-size: 11px; line-height: 14px; color: var(--bo-primary); pointer-events: none;
}
.bo-dash-chip {
  position: absolute; left: 6px; bottom: 6px; display: inline-flex; align-items: center; height: 22px; padding: 0 6px;
  border-radius: var(--bo-radius-sm); background: var(--bo-chip-bg); color: var(--bo-chip-fg);
  font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 700; pointer-events: none;
}
.bo-field.bo-has-chip select { padding-left: 52px; }

/* Second pass, live Kelola staging at 1568x720 (computed styles read in the browser). */
.bo-tabs a, .bo-tabs .off { font-weight: 500; color: var(--bo-text-nav); }
.bo-tabs a:hover, .bo-tabs a.on { color: var(--bo-primary-hover); }
.bo-tabs .off { color: var(--bo-text-muted); }
.bo-side h2 { font-size: 15px; color: var(--bo-text-nav); }
.bo-side a, .bo-side .off { font-weight: 500; }
.bo-shell > main .bo-subtabs a:not(.on) { color: var(--bo-text-muted); }
.bo-shell > main .bo-subtabs a:not(.on):hover { color: var(--bo-primary); }
.bo-shell > main td a:not(.btn):not([class*="bo-"]) { color: var(--bo-link); }
/* Dasbor rhythm: Kelola's hero starts 22px under the tab rule and its cards are 212px high. */
.bo-dash .bo-hero { margin-top: 22px; padding-top: 32px; }
.bo-dash .bo-hero > .bo-note { margin-top: 10px; }
.bo-dash .bo-filters { margin-top: 28px; }
.bo-dash .bo-filters .bo-field { width: 350px; }
/* Kelola's Dasbor: 344px dropdowns, tab bar 12px under the top bar, 54px tabs (measured 2026-10-15). */
.bo-dash .bo-filters .bo-multi { flex: none; width: 344px; max-width: none; }
.bo-shell > main.bo-dash { padding-top: 12px; }
.bo-dash > .bo-subtabs { margin-top: 0; }
.bo-dash > .bo-subtabs > a, .bo-dash > .bo-subtabs > span { height: 54px; box-sizing: border-box; }
.bo-dash .bo-filters .bo-field select { width: 100%; text-overflow: ellipsis; }
.bo-dash .bo-widgets { margin: 20px 0 6px; }
.bo-dash .bo-widget { padding-bottom: 36px; }
.bo-dash .bo-widget .nilai { margin: 29px 0 11px; }
.bo-dash .bo-filters select { white-space: nowrap; overflow: hidden; }
/* Master lists, Pemasok side-by-side: links styled as buttons keep no underline; the name column is left-aligned,
   name 500 with the code in small grey under it; the off switch no longer lets the knob cover "Tidak". */
a.bo-edit, a.bo-primary, a.bo-outline, a.bo-ghost { text-decoration: none; }
.bo-list.bo-table th.bo-left, .bo-list.bo-table td.bo-left { text-align: left; padding-left: 12px; }
.bo-list td.bo-left strong { font-weight: 500; }
.bo-list td.bo-left small { font-size: 11px; color: var(--bo-text-muted); }
.bo-switch:not(.on) { padding: 0 5px 0 0; font-size: 10px; }
/* Kelola's paginator sits centred: "Showing a to b of n", « ‹ pages › », rows per page. */
.bo-pager { justify-content: center; }
.bo-pager .count { margin-right: 12px; }
.bo-switch:not(.on) { font-size: 9px; }
.bo-searchbox { flex: 0 1 253px; min-width: 200px; }
.bo-toolbar select { flex: 0 1 190px; }
/* Aset Tetap filter row as Kelola draws it: OU and Periode are 340px floating-label fields, Kelompok Pajak drops to a second row. */
.bo-toolbar .bo-field.bo-flabel { flex: none; width: 340px; white-space: nowrap; }
.bo-toolbar .bo-field.bo-flabel select { width: 100%; max-width: none; }
.bo-toolbar .bo-field.bo-flabel .bo-date-input { width: 100%; }
.bo-toolbar .bo-field.bo-flabel .bo-daterange { display: flex; width: 100%; }
.bo-toolbar .bo-field.bo-flabel .bo-daterange input { flex: 1; min-width: 0; width: auto; background: var(--bo-field-bg); border: 1px solid var(--bo-field-bg); }
.bo-toolbar .bo-field ~ .bo-multi { order: 9; flex: 0 0 190px; }
/* Mitra on a GB02/GA02 draft (Step 7): beside Akun Kas/Bank, not pushed to the end. */
.bo-toolbar > .bo-mitra { display: flex; flex: 0 0 280px; }
.bo-mitra .bo-multi { max-width: none; }
/* Sub-tab underline: the rule is a background and the open tab's 2px underline paints over it (a negative margin inside
   overflow:auto clipped the underline away). */
.bo-subtabs { border-bottom: 0; background: linear-gradient(var(--bo-border-table), var(--bo-border-table)) bottom / 100% 2px no-repeat; }
.bo-subtabs a, .bo-subtabs .off { margin-bottom: 0; }
/* Cell chips in the master lists: Kelola's blue tag, bold; "#text" cells fill the column. */
.bo-list .bo-badge { font-weight: 700; }
.bo-chipw { display: block; max-width: 150px; margin: 0 auto; padding: 4px 8px; border-radius: var(--bo-radius-sm); background: var(--bo-chip-bg); color: var(--bo-chip-fg); font-weight: 700; text-align: center; }
.bo-download { cursor: default; }
.bo-toolbar > .bo-download:last-child, .bo-toolbar > .bo-download { margin-left: auto; }
.bo-primary.bo-download.off { opacity: 1; }
.bo-shell > main td .bo-hint a { color: var(--bo-text-muted); }
/* Produk list: name block left, type tag at the cell's right; the IDR tag left and the amount right in a price cell. */
.bo-list td.bo-namecell { display: table-cell; }
.bo-namecell { position: relative; padding-right: 56px !important; }
.bo-namecell > .bo-badge { position: absolute; right: 6px; top: 50%; transform: translateY(-50%); }
.bo-list td.bo-price { display: table-cell; }
.bo-price .bo-badge { float: left; }
.bo-price { text-align: right !important; }
/* Lihat Transaksi, side by side: bold unit column, blue "Butuh Tindakan" tag, underlined document numbers,
   blue "Tampilkan Semua", the activity cell right-aligned, 65px rows. */
.bo-txn td:first-child { font-weight: 700; }
.bo-txn td { padding-top: 14px; padding-bottom: 14px; }
.bo-txn .bo-tag.need { display: inline-block; min-width: 110px; padding: 6px 12px; box-sizing: border-box; background: var(--bo-chip-bg); color: var(--bo-chip-fg); font-size: var(--bo-fs-base); text-align: center; }
.bo-txn td.bo-mid > a { text-decoration: underline; }
.bo-expand summary { color: var(--bo-link); }
.bo-txn td.bo-activity { text-align: right; }
.bo-txn th:last-child { text-align: right; }
.bo-shell > main td .bo-hint a:not(.btn):not([class*="bo-"]) { color: var(--bo-text-muted); }
.bo-shell > main .bo-txn td.bo-mid > a:not(.btn):not([class*="bo-"]) { text-decoration: underline; }
/* Laporan filter row: floating-label fields like Kelola's, 37px rows. */
.bo-report-filter { align-items: center; margin-top: 24px; }
.bo-report-filter .bo-flabel { flex: 0 0 240px; }
.bo-report-filter .bo-flabel select, .bo-report-filter .bo-flabel input { width: 100%; box-sizing: border-box; }
.bo-report-filter .bo-field.bo-plain { flex: 1 1 260px; }
.bo-report td { padding-top: 9px; padding-bottom: 9px; }
/* Kelola's bare download icon at the end of the toolbar (Pengguna). */
.bo-more.bo-dl { display: grid; place-items: center; width: 34px; height: 34px; color: var(--bo-text-muted); cursor: default; }
a.bo-more.bo-dl { cursor: pointer; }
a.bo-more.bo-dl:hover { color: var(--bo-primary); background: var(--bo-field-bg); border-radius: var(--bo-radius); }
.bo-more.bo-dl svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.6; }
/* Breadcrumb: Kelola prints every segment 12px/600 in the muted grey, the open page included. */
.bo-crumb a, .bo-crumb strong, .bo-crumb span { font-weight: 600; color: var(--bo-text-muted); }
/* One-field date range (Kelola's range picker): two date inputs share a grey box with a dash between them. */
.bo-range1 { flex: 0 0 340px; background: var(--bo-field-bg); border-radius: var(--bo-radius); }
.bo-range1 input { flex: 1; min-width: 0; border-radius: 0 !important; background: transparent !important; border-color: transparent !important; }
.bo-range1 .bo-dash { color: var(--bo-text-muted); padding: 0 2px; }
.bo-report-filter .bo-rangefield { flex: 0 0 340px; }
.bo-report-filter .bo-rangefield .bo-range1 { flex: none; width: 100%; display: flex; }
.bo-report-filter .bo-rangefield .bo-range1 input { width: auto; }
/* Kelola wraps long column titles onto two lines instead of letting them run into the next column. */
.bo-list th, .bo-report th, .bo-list.bo-table th { white-space: normal; }
/* Konfigurasi side by side: chip colours, format segments, red cross, bare pencil icon. */
.bo-chipw.ok { background: var(--bo-success-soft); color: var(--bo-success-text); }
.bo-chipw.warn { background: var(--bo-warning-soft, #fff2e2); color: var(--bo-warning-text, #cc8925); }
.bo-seg b { display: inline-block; padding: 2px 6px; border-radius: var(--bo-radius-sm); background: var(--bo-border); color: var(--bo-text-muted); font-size: 11px; font-weight: 700; }
.bo-seg i { padding: 0 4px; font-style: normal; color: var(--bo-text-muted); }
.bo-no { color: var(--bo-danger); font-size: 14px; font-weight: 700; }
.bo-edit.bo-iconedit { background: none; border-color: transparent; color: var(--bo-link); padding: 0 6px; }
.bo-edit.bo-iconedit span { display: none; }
.bo-edit.bo-iconedit:hover { background: var(--bo-surface-hover); border-color: transparent; color: var(--bo-link); }
/* Akses Halaman: the three tree actions sit together at the left; tree rows are compact with a caret. */
.bo-pa-bar > * { flex: none; width: auto; }
.bo-pa-bar { gap: 24px; justify-content: flex-start; }
.bo-pa-area { margin: 0; }
.bo-pa-area > summary, .bo-pa-group > summary { padding: 4px 0; min-height: 0; list-style: none; }
.bo-pa-area > summary::-webkit-details-marker, .bo-pa-group > summary::-webkit-details-marker { display: none; }
.bo-pa-area > summary::before, .bo-pa-group > summary::before { content: ""; width: 6px; height: 6px; margin: 0 6px 0 2px; border: solid var(--bo-text-muted); border-width: 0 1.6px 1.6px 0; transform: rotate(-45deg); transition: transform .12s; }
.bo-pa-area[open] > summary::before, .bo-pa-group[open] > summary::before { transform: rotate(45deg); }
.bo-pa-item { padding-top: 3px; padding-bottom: 3px; }
.bo-pa-area input[type=checkbox], .bo-pa-group input[type=checkbox], .bo-pa-item input[type=checkbox] { width: 16px; height: 16px; min-width: 0; min-height: 0; }

/* Marketing dashboard (marketing.html) — Google reviews. `.stars` is the one
   primitive this page invents: nothing else in the app draws a rating, and a
   rating without its digits is unreadable anyway, so the glyph leads and the
   number carries. Monochrome like the rest — no new accent colour for one page.
   Reuses .outlet-table for both cards rather than a second table style. */
.stars { font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* 1–5 counts beside each other: "1 22 · 2 5 · 3 9 · 4 40 · 5 256", so a
   spread-out 5-star average and a solid one look different at a glance. */
.star-dist { display: inline-flex; gap: 9px; font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.star-dist .sd i { font-style: normal; margin-right: 3px; opacity: .7; }
.star-dist .sd b { font-weight: 600; color: var(--ink); }
/* The review text runs long; one clipped line keeps the ten rows comparable,
   with the full comment visible in Google. */
.review-comment { display: block; max-width: 52ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.review-by { display: block; font-size: 12px; color: var(--muted); }

/* Marketing › 3 Star (templates/marketing/review-inbox.html) and its entry button on Data Complain.
   Same family as the page's own buttons: "Update" is #options-open (outline, --tap tall, 20px padding),
   "New Complain" the brown filled one. An <a> needs the box a <button> already gets, and the global
   `button` rule would make every button full width and brown, so each is sized explicitly here. */
.page-actions form { margin: 0; }
.page-actions .nc-secondary { width: auto; height: var(--tap); padding: 0 20px; }
a.nc-secondary {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: var(--tap); padding: 0 20px; border-radius: 8px;
  font-size: 16px; font-weight: 650; text-decoration: none; cursor: pointer;
}
.nc-secondary:hover { background: var(--line-soft); }
/* Waiting-for-a-check count on the 3 Star button. */
.pill {
  display: inline-block; min-width: 22px; padding: 0 7px; border-radius: 11px;
  background: var(--brand); color: #fff; font-size: 12px; font-weight: 700; line-height: 22px; text-align: center;
  font-variant-numeric: tabular-nums;
}
/* ✓ files, ✕ leaves: two square buttons side by side in the table's last cell. */
.review-act { display: inline-flex; gap: 6px; }
.review-act form { margin: 0; }
.review-act button { width: 38px; height: 38px; min-height: 0; padding: 0; border-radius: 8px; font-size: 17px; line-height: 1; }
.review-act button.ok { background: var(--brand); color: #fff; }
.review-act button.ok:hover { background: var(--brand-dark); }
/* Kembalikan (undo) in the Diabaikan table: a small outline button, not a full-width brown one. */
button.row-btn { width: auto; min-height: 0; height: 34px; padding: 0 14px; border-radius: 8px; font-size: 14px; }
.review-sub { margin: 4px 0 6px; font-size: 18px; }

/* HRD · Recruitment (/hrd/recruitment, /hrd/recruitment/{id}).
   Summary tiles + the two list tables. The rekrutmen/kandidat tables use the
   fixed-layout pattern (widths pinned per column so filtering never
   re-measures — see .users-table.mutasi's note). The posisi column and field
   are deliberately louder than their neighbours: HR asked for posisi to be
   highlighted, and it is the field that decides which funnel the candidates
   walk. */
/* The summary module: one rectangle answering "what is needed where" — the
   three headline numbers in a divided band (peers across, value below; the
   soft --line-soft rule is the within-group column rule), the per-outlet block
   under them on the shared .outlet-table. The band is the page's only
   emphasis, so nothing else on the page competes with it. */
.ringkasan { margin: 0 0 26px; border: 1px solid var(--line); background: var(--field); }
.ringkasan .stat-band { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.ringkasan .stat { min-width: 0; padding: 14px 16px; }
.ringkasan .stat + .stat { border-left: 1px solid var(--line-soft); }
.ringkasan .stat-label {
  margin: 0 0 4px; font-size: 12px; font-weight: 600; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--muted);
}
.ringkasan .stat-angka {
  margin: 0; font-size: clamp(26px, 2vw, 34px); font-weight: 700; line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.ringkasan .stat .hint { margin: 4px 0 0; font-size: 12px; }
.ringkasan-bawah { padding: 14px 16px 6px; border-top: 1px solid var(--line); }
.ringkasan-bawah .hint { margin: 10px 0 0; font-size: 12px; }
@media (max-width: 720px) {
  .ringkasan .stat-band { grid-template-columns: 1fr; }
  .ringkasan .stat + .stat { border-left: 0; border-top: 1px solid var(--line-soft); }
}

.posisi-hl { font-weight: 700; color: var(--brand-dark); }

/* Operasional · Permintaan Karyawan — the ask-for-staff form. The house
   field-grid language (finance's .rate-form boxes: uniform fields on --field,
   12px muted labels, square hairlines) in the archive's .grup card, but its
   own class so neither surface's look moves. Three columns wide, two on
   medium, one on narrow — a fixed grid, so filling the form never re-measures
   the page. The posisi field carries the page's only accent (HR: "posisi
   perlu di highlight") — it decides which funnel the candidates will walk. */
.form-minta { margin: 0 0 28px; max-width: 1100px; }  /* snug on a wide page: the tracker reads wide, the form keeps the classic measure */
.form-minta .grup { margin: 0; border: 1px solid var(--line); background: var(--field); }
.form-minta .grup-judul {
  margin: 0; padding: 12px 16px; border-bottom: 1px solid var(--line);
  font-size: 12px; font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--muted);
}
.form-minta .isian {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px; padding: 16px;
}
.form-minta label {
  display: flex; flex-direction: column; gap: 6px; margin: 0;
  font-size: 12px; font-weight: 600; color: var(--muted);
}
.form-minta .full { grid-column: span 2; }  /* fills the row beside Due date; full row at 2-col */
/* The detail page's two-field mini form (Tambah kandidat): two columns, and a
   narrower measure than the full request form. */
.form-minta .isian.dua { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.form-minta.mini { max-width: 720px; }
.form-minta input, .form-minta select {
  width: 100%; box-sizing: border-box; min-height: var(--tap); margin: 0; padding: 8px 10px;
  font: inherit; font-size: 16px;  /* iOS zooms on focus below 16px */
  color: var(--ink); background: var(--field); border: 1px solid var(--line); border-radius: 0;
}
.form-minta input[type=number] { font-variant-numeric: tabular-nums; }
.form-minta input:user-invalid { border-color: #7a2d18; outline: 1px solid #7a2d18; }
.form-minta label.posisi-field > span { color: var(--brand-dark); }
.form-minta label.posisi-field select,
.form-minta label.posisi-field input {
  border-color: var(--brand); box-shadow: inset 3px 0 0 var(--brand); font-weight: 650;
}
/* Compact commit action, house style (page-actions, .konfirmasi-aksi) — not a
   full-width template bar. */
.form-minta > button { width: auto; margin-top: 16px; padding: 0 22px; }
@media (min-width: 721px) and (max-width: 1080px) {
  .form-minta .isian { grid-template-columns: 1fr 1fr; }
  /* Beside Due date at two columns too — no orphan cell in the last row. */
  .form-minta .full { grid-column: span 1; }
}
@media (max-width: 720px) {
  .form-minta .isian, .form-minta .isian.dua { grid-template-columns: 1fr; }
}

/* Operasional · Permintaan Karyawan (/operasional/recruitment-request),
   redesigned 2026-10-13. Plain underline tabs under the title, one hairline
   card per request (facts row, then each candidate with their stage in words
   and the "jejak" stepper), and a form with fieldsets beside a "Setelah
   dikirim" aside. Square, hairlined, one brown accent: the Swiss reskin, no
   new tokens. The stepper states come from RecruitmentQuery.Kandidat#langkah;
   this sheet only draws them. */
.page.minta-halaman { width: 100%; max-width: 1180px; }
.minta-kepala h1 { margin: 0 0 6px; }
.minta-kepala p { margin: 0; max-width: 64ch; font-size: 14px; line-height: 1.5; color: var(--muted); }

.minta-tab { display: flex; gap: 28px; margin: 24px 0 28px; border-bottom: 1px solid var(--line); }
.minta-tab a {
  display: inline-flex; align-items: center; gap: 8px;
  margin-bottom: -1px; padding: 10px 0 12px; border-bottom: 2px solid transparent;
  font-size: 14px; font-weight: 650; color: var(--muted); text-decoration: none;
}
.minta-tab a:hover { color: var(--ink); }
.minta-tab a.active { color: var(--ink); border-bottom-color: var(--brand); }
.minta-tab a:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.minta-tab b {
  min-width: 22px; padding: 1px 6px; text-align: center;
  font-size: 12px; font-variant-numeric: tabular-nums; background: var(--line-soft); color: var(--ink);
}

.minta-kartu { margin: 0 0 20px; border: 1px solid var(--line); background: var(--field); }
.minta-kartu-kepala {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
  padding: 20px 24px 18px;
}
.minta-kartu-kepala h2 { margin: 0; font-size: 24px; font-weight: 700; letter-spacing: -0.01em; line-height: 1.15; }
.minta-jumlah { margin-left: 6px; font-weight: 600; color: var(--muted); font-variant-numeric: tabular-nums; }
.minta-outlet { display: block; margin-top: 5px; font-size: 15px; font-weight: 500; color: var(--muted); letter-spacing: 0; }
/* Terbuka = amber, Terpenuhi = green, Dibatalkan = grey. Dark text on a pale tint
   keeps each pair above 4.5:1 (amber #5c4300 on #fff1bf, green #1e4d28 on #dcefdc). */
.minta-status {
  flex: none; padding: 5px 10px; border: 1px solid #c99700; background: #fff1bf; color: #5c4300;
  font-size: 12px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
}
.minta-status.status-FILLED { background: #dcefdc; border-color: #3b7d47; color: #1e4d28; }
.minta-status.status-CANCELLED { background: var(--field); border-color: var(--line); color: var(--muted); }

.minta-fakta {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  margin: 0; border-top: 1px solid var(--line);
}
.minta-fakta > div { padding: 14px 24px; border-right: 1px solid var(--line-soft); min-width: 0; }
.minta-fakta > div:last-child { border-right: 0; }
.minta-fakta dt { margin: 0 0 4px; font-size: 12px; font-weight: 600; color: var(--muted); }
.minta-fakta dd { margin: 0; font-size: 15px; font-weight: 650; font-variant-numeric: tabular-nums; }
.lewat-due { display: inline-block; margin-left: 6px; color: #a3301a; font-size: 12px; font-weight: 700; }
.minta-bar { display: block; height: 4px; margin-top: 8px; background: var(--line-soft); }
.minta-bar i { display: block; height: 100%; background: var(--brand); }

.minta-kandidat {
  display: grid; grid-template-columns: 210px minmax(0, 1fr); gap: 24px; align-items: center;
  padding: 18px 24px; border-top: 1px solid var(--line);
}
.minta-kandidat + .minta-kandidat { border-top-color: var(--line-soft); }
.minta-kandidat-id { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.kandidat-nama { font-size: 16px; font-weight: 650; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.minta-tahap { font-size: 14px; font-weight: 650; color: var(--brand); }
.minta-tahap-no { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.minta-belum { margin: 0; padding: 16px 24px; border-top: 1px solid var(--line); font-size: 14px; color: var(--muted); }

.jejak { display: flex; list-style: none; margin: 0; padding: 0; min-width: 0; }
.jejak li {
  flex: 1; position: relative; min-width: 0;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
}
.jejak li:not(:last-child)::after {
  content: ""; position: absolute; top: 14px; left: calc(50% + 18px);
  width: calc(100% - 36px); height: 2px; background: var(--line);
}
.jejak li.lewat:not(:last-child)::after { background: var(--brand); }
.jejak .bundar {
  width: 30px; height: 30px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums;
  background: var(--field); border: 1.5px solid var(--line); color: var(--muted);
}
.jejak li.lewat .bundar { background: var(--brand); border-color: var(--brand); color: #fff; }
.jejak li.kini .bundar { border: 3px solid var(--brand); color: var(--brand); }
.jejak .label-tahap { font-size: 12px; line-height: 1.25; text-align: center; color: var(--ink); }
.jejak li:not(.lewat):not(.kini) .label-tahap { color: var(--muted); }
.jejak li.kini .label-tahap { font-weight: 700; }

.minta-kosong { max-width: 680px; padding: 32px; border: 1px solid var(--line); background: var(--field); }
.minta-kosong h2 { margin: 0 0 8px; font-size: 22px; font-weight: 700; letter-spacing: -0.01em; }
.minta-kosong > p { margin: 0; max-width: 56ch; font-size: 15px; line-height: 1.5; color: var(--muted); }

.minta-alur { list-style: none; margin: 24px 0; padding: 0; display: grid; gap: 16px; counter-reset: alur; }
.minta-alur li {
  counter-increment: alur;
  display: grid; grid-template-columns: 28px minmax(0, 1fr); column-gap: 14px; row-gap: 2px;
}
.minta-alur li::before {
  content: counter(alur); grid-row: span 2;
  width: 28px; height: 28px; display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--line); font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums;
}
.minta-alur strong { font-size: 14px; }
.minta-alur span { font-size: 13px; line-height: 1.45; color: var(--muted); }

.minta-baru { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 48px; align-items: start; }
.minta-bagian { margin: 0 0 30px; padding: 0; border: 0; min-width: 0; }
.minta-bagian legend {
  width: 100%; margin: 0 0 18px; padding: 0 0 10px; border-bottom: 1px solid var(--line);
  font-size: 15px; font-weight: 700;
}
.minta-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; }
.minta-grid .penuh { grid-column: 1 / -1; }
.minta-form label { display: flex; flex-direction: column; gap: 6px; margin: 0; font-weight: 400; }
.minta-form label > span { font-size: 13px; font-weight: 650; }
.minta-form small { font-size: 12px; line-height: 1.4; color: var(--muted); }
.minta-form input, .minta-form select, .minta-form textarea {
  width: 100%; box-sizing: border-box; min-height: var(--tap); margin: 0; padding: 8px 10px;
  font: inherit; font-size: 16px;  /* iOS zooms on focus below 16px */
  color: var(--ink); background: var(--field); border: 1px solid var(--line); border-radius: 0; font-weight: 400;
}
.minta-form textarea { min-height: 88px; resize: vertical; line-height: 1.45; }
.minta-form input[type=number] { font-variant-numeric: tabular-nums; }
.minta-form input:focus-visible, .minta-form select:focus-visible, .minta-form textarea:focus-visible {
  outline: 2px solid var(--brand); outline-offset: 1px; border-color: var(--brand);
}
.minta-form input:user-invalid, .minta-form select:user-invalid { border-color: #7a2d18; }
/* The page's one accent: posisi decides which funnel the candidates walk. */
.minta-form .posisi-field > span { color: var(--brand-dark); }
.minta-form .posisi-field select, .minta-form .posisi-field input { border-color: var(--brand); font-weight: 650; }
.minta-aksi { padding-top: 4px; }
.minta-aksi .btn { width: auto; padding: 0 28px; }

.minta-samping { position: sticky; top: 24px; padding: 22px 22px 6px; border: 1px solid var(--line); background: var(--field); }
.minta-samping h2 { margin: 0 0 16px; font-size: 15px; font-weight: 700; }
.minta-samping .minta-alur { margin: 0 0 18px; }

@media (max-width: 960px) {
  .minta-baru { grid-template-columns: minmax(0, 1fr); gap: 28px; }
  .minta-samping { position: static; }
  .minta-kandidat { grid-template-columns: minmax(0, 1fr); gap: 12px; }
}
@media (max-width: 640px) {
  .minta-grid { grid-template-columns: minmax(0, 1fr); }
  .minta-kartu-kepala, .minta-kandidat, .minta-belum { padding-left: 16px; padding-right: 16px; }
  .minta-fakta > div { padding: 12px 16px; }
  .minta-kosong { padding: 22px 18px; }
  /* Eight steps do not fit a phone: the funnel scrolls sideways instead of crushing its labels. */
  .jejak { overflow-x: auto; padding-bottom: 4px; }
  .jejak li { flex: 0 0 76px; }
  .jejak .label-tahap { font-size: 11px; }
}

/* The per-outlet table rides the shared .outlet-table (dashboard/outlet.html's
   class — no page-local variant; an earlier local override here leaked into
   that surface and is gone). Its caption names the block. */

.users-table.rekrutmen { table-layout: fixed; min-width: 1000px; }
.users-table.rekrutmen th:nth-child(1) { width: 4%; }
.users-table.rekrutmen th:nth-child(2) { width: 14%; }
.users-table.rekrutmen th:nth-child(3) { width: 11%; }
.users-table.rekrutmen th:nth-child(4) { width: 11%; }
.users-table.rekrutmen th:nth-child(5) { width: 14%; }
.users-table.rekrutmen th:nth-child(6) { width: 11%; }
.users-table.rekrutmen th:nth-child(7) { width: 13%; }
.users-table.rekrutmen th:nth-child(8) { width: 9%; }
.users-table.rekrutmen th:nth-child(9) { width: 13%; }
.users-table.rekrutmen td { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.users-table.rekrutmen th.aksi-cell,
.users-table.rekrutmen td.aksi-cell { text-align: center; overflow: visible; }
.users-table.rekrutmen td.aksi-cell form { display: inline-block; margin: 0 0 0 8px; }

.users-table.kandidat { table-layout: fixed; min-width: 900px; }
.users-table.kandidat th:nth-child(1) { width: 4%; }
.users-table.kandidat th:nth-child(2) { width: 18%; }
.users-table.kandidat th:nth-child(3) { width: 13%; }
.users-table.kandidat th:nth-child(4) { width: 10%; }
.users-table.kandidat th:nth-child(5) { width: 17%; }
.users-table.kandidat th:nth-child(6) { width: 9%; }
.users-table.kandidat th:nth-child(7) { width: 9%; }
.users-table.kandidat th:nth-child(8) { width: 20%; }
.users-table.kandidat td { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.users-table.kandidat td:nth-child(7),
.users-table.kandidat td.edit-cell { overflow: visible; }
/* Rows align to the top: the riwayat cell grows when its log is open, and a
   vertically-centred name beside a tall log block reads as floating. */
.users-table.kandidat td { vertical-align: top; padding-top: 10px; }
.users-table.kandidat tbody tr.is-armed td { background: #f8f1ee; border-bottom-color: var(--brand); }
.users-table.kandidat tbody tr:has(+ tr.is-armed) td { border-bottom-color: var(--brand); }
.users-table.kandidat tbody tr:not(.is-armed):hover td { background: #f6f6f6; }
/* The riwayat log inside a candidate row: date in a fixed tabular column, the
   move in ink, who in muted, and a note on its own aligned line — one entry
   per visual line, never wrapped mid-entry. */
ul.riwayat { margin: 6px 0 0; padding-left: 18px; font-size: 13px; white-space: normal; }
ul.riwayat li {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 10px; margin: 5px 0;
}
ul.riwayat .r-tanggal { flex: none; width: 86px; font-variant-numeric: tabular-nums; }
ul.riwayat .r-gerak { font-weight: 600; }
ul.riwayat .r-oleh { color: var(--muted); font-size: 12px; }
ul.riwayat .r-catatan {
  flex-basis: 100%; padding-left: 96px; color: var(--muted); font-size: 12px; font-style: italic;
}

/* HR · Recruitment redesign (2026-10-13): request list, outlet need, and the
   request's candidate page. Same square badge (.minta-status) and divided facts
   row (.minta-fakta) as the requester's page; this block only adds what the HR
   tables need. */
.rekrutmen td strong { display: block; font-weight: 650; }
.rekrutmen td small { display: block; margin-top: 2px; font-size: 12px; color: var(--muted); }
.rekrutmen td small.x-jumlah { display: inline; margin: 0 0 0 6px; font-size: 13px; font-weight: 600; }
.rekrutmen td small.lewat-due { margin: 3px 0 0; color: #a3301a; font-weight: 700; }
.rekrutmen td .minta-bar { width: 100%; max-width: 110px; margin-top: 6px; }
.btn.btn-kecil { min-height: 32px; padding: 0 14px; font-size: 13px; }
.users-table.rekrutmen th.aksi-cell, .users-table.rekrutmen td.aksi-cell { text-align: left; white-space: nowrap; }
.aksi-cell .aksi-link.batal { color: var(--muted); font-weight: 600; }
.aksi-cell .aksi-link.batal:hover { color: #a3301a; }

.outlet-kebutuhan { margin-top: 40px; }
.outlet-kebutuhan .outlet-table { table-layout: fixed; }
.outlet-kebutuhan .outlet-table tr > :first-child { width: 40%; }
.outlet-kebutuhan .ada-minta { font-weight: 700; color: var(--brand-dark); }
.outlet-lain { margin: 14px 0 0; }
.outlet-lain summary { cursor: pointer; font-size: 13px; font-weight: 600; color: var(--muted); }
.outlet-lain summary:hover { color: var(--ink); }
.outlet-lain summary:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.outlet-lain .outlet-table { margin-top: 8px; }
/* .hint carries a -12px top margin meant to tuck it under a heading; here it sat on top of the folded summary. */
.outlet-kebutuhan > .hint { margin: 14px 0 0; }

.kembali { display: inline-block; margin: 0 0 12px; font-size: 13px; text-decoration: none; }
.kembali::before { content: "\2190\00a0"; }
.kembali:hover { text-decoration: underline; }
.detail-fakta { margin-bottom: 14px; }
.detail-fakta .minta-fakta { border-top: 0; }
.minta-fakta small { display: block; margin-top: 2px; font-size: 12px; font-weight: 500; color: var(--muted); }
.detail-catatan { margin: 0 0 6px; max-width: 90ch; font-size: 13px; line-height: 1.5; color: var(--muted); }
.page-head .minta-outlet { font-size: 15px; }
.judul-kandidat { display: flex; align-items: baseline; gap: 10px; }
.judul-kandidat small { font-size: 13px; font-weight: 600; color: var(--muted); font-variant-numeric: tabular-nums; }

.tahap-nama { font-weight: 650; color: var(--brand); }
.tahap-no { margin-left: 8px; font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.tahap-seg { display: flex; gap: 2px; max-width: 168px; margin-top: 8px; }
.tahap-seg i { flex: 1; height: 4px; background: var(--line); }
.tahap-seg i.lewat { background: #a58a7f; }
.tahap-seg i.kini { height: 6px; margin-top: -1px; background: var(--brand); }

dialog#tambah-dialog { width: min(680px, calc(100vw - 48px)); max-height: calc(100vh - 48px); overflow: auto; }
.tambah-kandidat .tambah-untuk { margin: 0 0 18px; font-size: 14px; color: var(--muted); }
.tambah-kandidat .tambah-untuk strong { color: var(--ink); }
.tambah-baris { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; align-items: end; }
.tambah-baris .penuh { grid-column: 1 / -1; }
.buka-tambah { margin-left: auto; width: auto; }
@media (max-width: 720px) {
  .tambah-baris { grid-template-columns: minmax(0, 1fr); }
}
.kandidat .profil, .pelamar .profil { display: block; margin-top: 2px; font-size: 12px; color: var(--muted); }
@media (max-width: 720px) {
  .tambah-baris { grid-template-columns: minmax(0, 1fr); }
  .tambah-aksi .btn { width: 100%; }
}

/* The two HR recruitment tables have a floor width; without min-width: 0 the page
   column grew to fit them and the whole page scrolled sideways on a phone instead
   of the .tabel-scroll box. */
.page.wide:has(.users-table.rekrutmen),
.page.wide:has(.users-table.kandidat) { min-width: 0; max-width: 100%; }

/* Database pelamar tab: eight columns; the name is the row's link, so no action
   column. Header filters borrow the arsip-table funnel (label left, funnel on the
   right edge) so the headers sit on one line. */
.users-table.pelamar { table-layout: fixed; min-width: 1000px; }
.users-table.pelamar th:nth-child(1) { width: 4%; }
.users-table.pelamar th:nth-child(2) { width: 10%; }
.users-table.pelamar th:nth-child(3) { width: 20%; }
.users-table.pelamar th:nth-child(4) { width: 11%; }
.users-table.pelamar th:nth-child(5) { width: 11%; }
.users-table.pelamar th:nth-child(6) { width: 13%; }
.users-table.pelamar th:nth-child(7) { width: 13%; }
.users-table.pelamar th:nth-child(8) { width: 18%; }
.users-table.pelamar td { vertical-align: top; padding-top: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.users-table.pelamar td.tahap-cell { overflow: visible; }
.users-table.pelamar th { position: relative; padding: 0; }
.users-table.pelamar th.c-no { padding: 8px 10px; }
.users-table.pelamar th:not(.c-no):not(:has(> .th-label)) { padding: 8px 10px; }
.users-table.pelamar th.cf-open { z-index: 5; }
.users-table.pelamar th > .cf-btn { position: absolute; right: 4px; top: 50%; transform: translateY(-50%); width: 26px; height: 26px; justify-content: center; }
.users-table.pelamar .th-label { display: flex; align-items: center; min-height: 40px; padding: 8px 32px 8px 10px; box-sizing: border-box; letter-spacing: 0.04em; }
.tabel-scroll:has(.users-table.pelamar) { min-height: 320px; }
.users-table.pelamar .profil { overflow: hidden; text-overflow: ellipsis; }
.users-table.pelamar .nama-link { color: var(--ink); font-weight: 600; text-decoration: none; }
.users-table.pelamar .nama-link:hover, .users-table.pelamar .nama-link:focus-visible { color: var(--brand); text-decoration: underline; }
.users-table.pelamar tbody tr:hover td { background: #f6f6f6; }
.page.wide:has(.users-table.pelamar) { min-width: 0; max-width: 100%; }

/* Eliminated candidate: the row recedes, the result stays legible as a small
   red-ink tag, the stage bar stops in red where they left. */
.users-table tr.gugur td { color: var(--muted); }
.users-table tr.gugur .nama-cell, .users-table tr.gugur .nama-link { font-weight: 400; }
.gugur-tag { display: inline-block; padding: 1px 6px; border: 1px solid #a3301a; color: #a3301a;
  font-size: 11px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; }
.gugur-info { display: block; margin-top: 6px; font-size: 12px; color: var(--muted); }
.gugur-info .gugur-tag { margin-right: 6px; }
.tahap-seg.gugur i.kini { background: #a3301a; }
.minta-kandidat-id .gugur-tag { margin-left: 8px; }
dialog#gugur-dialog { padding: 0; border: 0; border-radius: var(--radius); background: var(--field); color: var(--ink);
  width: min(520px, calc(100vw - 48px)); box-shadow: 0 24px 48px -12px rgba(36, 26, 21, 0.4); zoom: calc(1 / var(--z)); }
dialog#gugur-dialog::backdrop { background: rgba(20, 20, 20, 0.55); }
dialog#gugur-dialog form { padding: 26px 26px 0; }
dialog#gugur-dialog h2 { margin: 0 0 12px; font-size: 21px; font-weight: 700; line-height: 1.2; }
dialog#gugur-dialog .konfirmasi-teks { margin: 0 0 18px; font-weight: 650; }
.gugur-baris .penuh { grid-column: 1 / -1; }

/* Armed row, kandidat table: two actions (advance / eliminate) stack to the left
   of the pencil instead of running past the column edge. */
.users-table.kandidat tr.is-armed .row-actions.cell-edit { display: inline-flex; flex-direction: column; align-items: stretch;
  gap: 6px; vertical-align: top; margin-right: 8px; }
.users-table.kandidat tr.is-armed .row-actions form { display: block; }
.users-table.kandidat tr.is-armed .row-actions .switch { width: 100%; white-space: nowrap; }
.users-table.kandidat td.tahap-cell { white-space: normal; }
.users-table.kandidat .gugur-tag { white-space: nowrap; }

/* Recruitment: the applicant's own biodata form (link to copy at Screening, status, PDF). */
.kandidat .biodata { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; margin-top: 6px; font-size: 12px; }
.kandidat .biodata-status { padding: 1px 6px; border: 1px solid var(--line, #d7d7d7); color: var(--muted); white-space: nowrap; }
.kandidat .biodata-status.s-DRAFT { color: #8a5a00; border-color: #e0c58a; background: #fbf4e4; }
.kandidat .biodata-status.s-TERKIRIM { color: #1f6b3a; border-color: #a9d3b8; background: #e8f3ec; }
.kandidat .biodata-salin { width: auto; height: auto; min-height: 0; line-height: 1.4; display: inline-block; font: inherit; font-size: 12px; font-weight: 600; padding: 2px 8px; border: 1px solid #623627; background: #fff; color: #623627; cursor: pointer; border-radius: 0; }
.kandidat .biodata-salin:hover { background: #f3ece8; }
.kandidat .biodata-salin.ok { background: #623627; color: #fff; }
.kandidat .biodata-salin:disabled { opacity: .4; cursor: not-allowed; }
.kandidat .tes-unit { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.kandidat .biodata-pdf { color: #623627; font-weight: 600; text-decoration: underline; text-underline-offset: 2px; }

/* HRD — DISC / PAPI result (templates/hrd/psikotes-hasil.html). The verdict leads;
   evidence follows; PAPI's 20 factors are a scannable profile, long text on a toggle. */
.page.wide.psi { width: 100%; max-width: 1200px; } /* .page's auto margins would otherwise shrink it to its content */
.psi .vh { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.psi-kembali { display: inline-block; margin-bottom: 12px; }
.psi-kepala { margin-bottom: 12px; }
.psi-kepala h1 { flex: 1; min-width: 0; margin: 0; font-size: 24px; font-weight: 700; letter-spacing: -0.01em; line-height: 1.15; }
.psi-kepala .psi-cetak { flex: none; width: auto; height: auto; min-height: 32px; line-height: 1.4; display: inline-block; padding: 0 14px; border: 1px solid var(--line); background: var(--field); color: var(--brand);
  font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; border-radius: 0; }
.psi-kepala .psi-cetak:hover { background: #f3ece8; border-color: var(--brand); }
.psi-aksi { display: flex; flex: none; gap: 8px; }
.psi-kepala a.psi-cetak { display: inline-flex; align-items: center; text-decoration: none; }
.psi-kepala .psi-cetak:focus-visible, .psi-nav a:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.psi-catatan { margin: 0 0 28px; max-width: 70ch; font-size: 13px; color: var(--muted); }

.psi-blok { margin: 0 0 36px; }
.psi-blok h2 { margin: 0 0 14px; padding-bottom: 8px; border-bottom: 2px solid var(--ink); font-size: 16px; font-weight: 700; letter-spacing: -0.005em; }
.psi-blok h2 i { font-style: normal; font-size: 13px; font-weight: 400; color: var(--muted); margin-left: 6px; }

/* DISC — the three readings side by side */
.psi-profil { display: grid; grid-template-columns: 1fr; gap: 0; }
.psi-profil > div { padding: 16px 0; border-bottom: 1px solid var(--line-soft); }
.psi-profil > div:last-child { border-bottom: 0; }
.psi-label { margin: 0 0 4px; font-size: 12px; font-weight: 600; color: var(--muted); }
.psi-pola { margin: 0 0 10px; font-size: 22px; font-weight: 700; line-height: 1.15; letter-spacing: -0.01em; color: var(--brand-dark); }
.psi-daftar { margin: 0; padding: 0; list-style: none; font-size: 14px; }
.psi-daftar li { padding: 6px 0; border-top: 1px solid var(--line-soft); }
.psi-daftar li:first-child { border-top: 0; padding-top: 0; }
@media (min-width: 860px) {
  .psi-profil { grid-template-columns: repeat(3, 1fr); }
  .psi-profil > div { padding: 4px 24px 4px 0; border-bottom: 0; }
  .psi-profil > div + div { padding-left: 24px; border-left: 1px solid var(--line-soft); }
}

/* DISC — evidence: tally + graph */
.psi-bukti { display: grid; grid-template-columns: 1fr; gap: 32px; }
.psi-tabel { width: 100%; border-collapse: collapse; font-size: 15px; font-variant-numeric: tabular-nums; }
.psi-tabel th, .psi-tabel td { padding: 9px 10px; border-bottom: 1px solid var(--line-soft); text-align: center; }
.psi-tabel thead th { font-size: 13px; font-weight: 700; color: var(--ink); vertical-align: bottom; border-bottom: 1px solid var(--line); }
.psi-tabel thead th small { display: block; margin-top: 2px; font-size: 11px; font-weight: 400; color: var(--muted); }
.psi-tabel tbody th { width: 52px; text-align: left; }
.psi-tabel td:last-child { font-weight: 700; }
.psi-huruf { display: inline-grid; place-items: center; width: 28px; height: 28px; background: var(--brand); color: #fff; font-weight: 700; font-size: 13px; }
.psi-grafik-kotak { max-width: 560px; }
.psi-grafik { width: 100%; height: auto; display: block; font-family: inherit; }
.psi-kisi { stroke: #e3e3e3; stroke-dasharray: 3 3; }
.psi-nol { stroke: #8a8a8a; stroke-width: 1.2; }
.psi-sumbu { font-size: 10px; fill: #6b6b6b; }
.psi-xhuruf { font-size: 14px; font-weight: 700; fill: #1a1a1a; }
.psi-seri { fill: none; stroke-width: 2; }
.psi-seri-0 { stroke: #623627; }
.psi-seri-1 { stroke: #1a1a1a; }
.psi-seri-2 { stroke: #6b6b6b; stroke-dasharray: 5 3; }
.psi-titik.psi-seri-0 { fill: #623627; stroke: #fff; }
.psi-titik.psi-seri-1 { fill: #1a1a1a; stroke: #fff; }
.psi-titik.psi-seri-2 { fill: #6b6b6b; stroke: #fff; }
.psi-legenda { list-style: none; margin: 12px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px 20px; font-size: 13px; color: var(--muted); }
.psi-legenda li { display: flex; align-items: center; gap: 8px; }
.psi-legenda i { display: inline-block; width: 11px; height: 11px; background: currentColor; }
.psi-legenda i.psi-seri-0 { color: #623627; border-radius: 50%; }
.psi-legenda i.psi-seri-1 { color: #1a1a1a; }
.psi-legenda i.psi-seri-2 { color: #6b6b6b; clip-path: polygon(50% 0, 100% 100%, 0 100%); }
@media (min-width: 960px) { .psi-bukti { grid-template-columns: minmax(300px, 380px) 1fr; gap: 56px; } }

/* DISC — analysis */
.psi-analisis { display: grid; grid-template-columns: 1fr; gap: 32px; }
.psi-prosa { margin: 0; max-width: 66ch; font-size: 15px; line-height: 1.6; }
@media (min-width: 860px) { .psi-analisis { grid-template-columns: 3fr 2fr; gap: 56px; } }

/* PAPI — jump nav + description toggle */
.psi-desk-cb { position: absolute; opacity: 0; width: 1px; height: 1px; }
.psi-alat { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 24px; margin-bottom: 28px;
  padding-bottom: 12px; border-bottom: 1px solid var(--line); }
.psi-nav { display: flex; flex-wrap: wrap; gap: 4px 20px; }
.psi-nav a { padding: 6px 0; border-bottom: 2px solid transparent; font-size: 13px; font-weight: 600; color: var(--muted); text-decoration: none; }
.psi-nav a:hover { color: var(--brand); border-bottom-color: var(--brand); }
.psi-toggle { display: inline-flex; align-items: center; gap: 8px; min-height: 32px; font-size: 13px; font-weight: 600; cursor: pointer; user-select: none; }
.psi-toggle::before { content: ""; width: 16px; height: 16px; border: 1px solid var(--ink); background: var(--field); box-sizing: border-box; }
.psi-desk-cb:checked ~ .psi-alat .psi-toggle::before { background: var(--brand) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-7' fill='none' stroke='%23fff' stroke-width='2'/%3E%3C/svg%3E") center / 14px no-repeat; border-color: var(--brand); }
.psi-desk-cb:focus-visible ~ .psi-alat .psi-toggle { outline: 2px solid var(--brand); outline-offset: 3px; }
html { scroll-padding-top: 16px; }

/* PAPI — ranked summary table */
.psi-ringkas td, .psi-ringkas th { text-align: left; vertical-align: top; }
.psi-ringkas thead th { vertical-align: bottom; }
.psi-ringkas tbody th { width: auto; font-weight: 600; }
.psi-ringkas td:first-child { width: 56px; }
.psi-ringkas .psi-angka { width: 64px; font-size: 18px; font-weight: 700; text-align: left; }
.psi-ringkas td:last-child { font-weight: 400; }
.psi-baris-teks { display: block; }
.psi-baris-teks + .psi-baris-teks { margin-top: 4px; padding-top: 4px; border-top: 1px solid var(--line-soft); }
@media (max-width: 599px) { .psi-ringkas th, .psi-ringkas td { padding: 8px 6px; font-size: 14px; } .psi-ringkas td:first-child { width: 40px; } .psi-ringkas .psi-angka { width: 40px; } }

/* PAPI — the 20 factors */
.psi-faktor { list-style: none; margin: 0; padding: 0; }
.psi-kolom-kepala { display: none; }
.psi-baris { display: grid; grid-template-columns: 32px 1fr; gap: 6px 14px; padding: 14px 0; border-bottom: 1px solid var(--line-soft); break-inside: avoid; }
.psi-kode { grid-row: 1 / span 3; display: grid; place-items: center; align-self: start; width: 28px; height: 28px; background: var(--brand); color: #fff; font-size: 13px; font-weight: 700; }
.psi-nama { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.psi-nama strong { font-size: 15px; font-weight: 650; }
.psi-nama span { font-size: 13px; color: var(--muted); }
.psi-skor { display: flex; align-items: center; gap: 10px; }
.psi-bar { position: relative; flex: 1; max-width: 220px; height: 8px; background: var(--line-soft); }
.psi-bar i { display: block; height: 100%; background: var(--brand); print-color-adjust: exact; -webkit-print-color-adjust: exact; }
.psi-skor b { min-width: 1.2em; font-size: 18px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1; }
.psi-tafsir { min-width: 0; overflow-wrap: anywhere; margin: 0; padding: 0; list-style: none; font-size: 15px; line-height: 1.4; }
.psi-tafsir li + li { margin-top: 4px; padding-top: 4px; border-top: 1px solid var(--line-soft); }
.psi-deskripsi { display: none; grid-column: 2; margin: 4px 0 0; max-width: 70ch; font-size: 13px; line-height: 1.55; color: var(--muted); }
.psi-desk-cb:checked ~ .psi-area .psi-deskripsi { display: block; }
@media (min-width: 900px) {
  .psi-kolom-kepala, .psi-baris { display: grid; grid-template-columns: 32px minmax(0, 1.1fr) 190px minmax(0, 1.7fr); gap: 4px 24px; align-items: start; }
  .psi-kolom-kepala { padding: 0 0 6px; font-size: 12px; font-weight: 600; color: var(--muted); border-bottom: 1px solid var(--line); }
  .psi-kode { grid-row: auto; }
  .psi-skor { padding-top: 3px; }
  .psi-deskripsi { grid-column: 2 / -1; }
}


/* DISC write-up (HR's report layout) */
.psi-laporan h3 { margin: 22px 0 8px; font-size: 15px; font-weight: 700; }
.psi-poin { margin: 0; padding-left: 22px; font-size: 15px; line-height: 1.55; max-width: 80ch; }
.psi-poin li { margin: 0 0 4px; }
.psi-data { margin: 0 0 36px; }
.psi-data > summary { cursor: pointer; margin: 0 0 20px; font-size: 14px; font-weight: 600; color: var(--muted); }

/* Print: the page only, descriptions included */
@media print {
  body:has(.psi) .side, body:has(.psi) .navtoggle, .psi-kembali, .psi-aksi, .psi-alat { display: none !important; }
  body:has(.psi) .shell { display: block; }
  .psi.page { max-width: none; padding: 0; margin: 0; }
  .psi-blok { margin-bottom: 18px; break-inside: avoid-page; }
  .psi-area, .psi-laporan { break-inside: auto; }
  .psi-blok h2, .psi-laporan h3 { break-after: avoid; }
  .psi-deskripsi { display: block !important; color: #333; }
  .psi-data { display: none; }
  .psi-kepala, .psi-baris { break-inside: avoid; }
  .psi-huruf, .psi-kode { print-color-adjust: exact; -webkit-print-color-adjust: exact; }
}

/* SQ - 4P audit, CCTV, plan, score (2026-10-13). Status colours are literal: red = miss, green = hit. */
.sq-tabs { display: flex; gap: 4px; margin: 0 0 16px; border-bottom: 1px solid var(--line); }
.sq-tabs a { padding: 10px 14px; color: var(--muted); text-decoration: none; border-bottom: 2px solid transparent; margin-bottom: -1px; }
.sq-tabs a.on { color: var(--ink); border-bottom-color: var(--brand); font-weight: 600; }
.sq-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.sq-table tfoot th { border-top: 2px solid var(--line); }
.sq-tag { margin-left: 6px; font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }
.sq-score { font-weight: 700; }
.sq-hit { color: #1f7a3d; }
.sq-miss { color: #b3261e; }
/* Visit plan: outlets down, weeks across; a cell is one outlet-week. Hairline grid, no cards. */
.sq-planbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 24px; margin: 4px 0 12px; }
.sq-month { display: flex; align-items: center; gap: 12px; }
.sq-month h2 { margin: 0; min-width: 10ch; font-size: 20px; line-height: 32px; text-align: center; }
.sq-step { display: inline-flex; align-items: center; justify-content: center; width: var(--tap); height: var(--tap);
           color: var(--ink); background: var(--field); border: 1px solid var(--line); text-decoration: none; }
.sq-step:hover { background: var(--line-soft); }
.sq-step:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.sq-planstat { margin: 0; font-size: 14px; color: var(--muted); }
.sq-planstat b { margin-left: 16px; font-size: 20px; color: var(--ink); font-variant-numeric: tabular-nums; }
.sq-plan { width: 100%; min-width: 760px; border-collapse: collapse; table-layout: fixed; }
.sq-plan th, .sq-plan td { padding: 0; border: 1px solid var(--line); vertical-align: top; text-align: left; }
.sq-plan thead th { padding: 10px 12px; font-size: 12px; font-weight: 400; color: var(--muted); }
.sq-plan thead th:first-child { width: 168px; }
.sq-plan thead b { display: block; font-size: 14px; color: var(--ink); }
.sq-plan thead th.now { box-shadow: inset 0 -3px 0 var(--brand); }
.sq-plan tbody th { position: sticky; left: 0; z-index: 1; padding: 0 12px; height: 64px; background: var(--bg);
                    font-size: 14px; font-weight: 600; vertical-align: middle; }
.sq-plan-split th { height: auto !important; padding: 18px 12px 8px !important; border-left: 0 !important; border-right: 0 !important;
                    font-size: 12px !important; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); }
.sq-cell { height: 64px; }
.sq-cell form { margin: 0; height: 100%; }
.sq-cell-add { width: 100%; height: 100%; min-height: 64px; padding: 0 12px; border: 0; background: none; cursor: pointer;
               text-align: left; font: inherit; font-size: 13px; font-weight: 400; color: transparent; }
.sq-cell-add::before { content: "+ "; color: var(--muted); font-size: 16px; }
.sq-cell-add:hover, .sq-cell-add:focus-visible { background: #f5f5f5; color: var(--ink); }
.sq-cell.is-planned, .sq-cell.is-done, .sq-cell.is-missed { padding: 8px 12px; }
.sq-cell.is-planned { background: var(--line-soft); }
.sq-cell-state { display: block; font-size: 14px; }
.sq-cell.is-done .sq-cell-state { color: #1f7a3d; }
.sq-cell.is-missed .sq-cell-state { color: #b3261e; }
.sq-cell-acts { display: flex; align-items: center; gap: 14px; margin: 6px 0 0; font-size: 13px; }
.sq-cell-acts a { color: var(--ink); }
.sq-cell-acts form { height: auto; }
.sq-tindakan span { display: block; font-size: 13px; color: var(--muted); margin-bottom: 4px; }
.sq-pillar { margin: 28px 0 4px; font-size: 18px; border-bottom: 2px solid var(--brand); padding-bottom: 4px; }
.sq-group { margin: 16px 0 4px; font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); }
.sq-item { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 260px); gap: 8px 16px; align-items: center; padding: 8px 0; border-bottom: 1px solid var(--line-soft); }
.sq-q { margin: 0; }
.sq-scale { display: flex; gap: 4px; }
.sq-scale label { position: relative; margin: 0; }
/* The native radio fills its label so click/keyboard/AT keep working while the
   square span carries the look. Without inset:0 + width/height it inherited the
   global `input { width: 100% }` from the unpositioned label and became a
   ~viewport-wide invisible box that spilled over — and swallowed clicks on — the
   Keterangan note beside it. Same idiom as .pnl-view above. */
.sq-scale input { position: absolute; inset: 0; width: 100%; height: 100%; min-height: 0; margin: 0; padding: 0; opacity: 0; cursor: pointer; }
.sq-scale span { display: inline-flex; width: 40px; height: 40px; align-items: center; justify-content: center; border: 1px solid var(--line); cursor: pointer; user-select: none; }
.sq-scale input:checked + span { background: var(--brand); border-color: var(--brand); color: #fff; font-weight: 700; }
.sq-scale input:focus-visible + span { outline: 2px solid var(--brand); outline-offset: 2px; }
/* margin:0 — the global `input { margin-bottom: 18px }` is a login-form rule; inside
   .sq-item's `align-items:center` grid the 18px bottom margin shifts the note up
   ~9px so it never lines up with the 1-5 squares beside it. */
.sq-note { width: 100%; margin: 0; }
.sq-tindakan { display: block; margin: 20px 0; }
.sq-tindakan textarea { width: 100%; }
.sq-total { position: sticky; bottom: 0; display: flex; flex-wrap: wrap; gap: 24px; align-items: baseline; padding: 12px 16px; background: var(--bg); border-top: 2px solid var(--brand); }
.sq-total small { display: block; font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }
.sq-total b { font-size: 18px; font-variant-numeric: tabular-nums; }
.sq-grand b { font-size: 28px; }
.sq-actions { margin: 12px 0 24px; }
/* CCTV input: entry (left, leads with the outlet) beside the month board (right, also the outlet
   switcher). Both columns open on the same h2 so their first lines align. */
.sq-input { display: grid; grid-template-columns: minmax(0, 1fr) minmax(340px, 32%); gap: 48px; align-items: start; }
.sq-h { margin: 0 0 16px; font-size: 20px; line-height: 32px; }
.sq-ctx { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 20px; margin: 0 0 20px; padding: 0 0 12px; border-bottom: 2px solid var(--brand); }
.sq-ctx .sq-h { margin: 0; }
.sq-side .sq-h { margin: 0 0 20px; padding: 0 0 12px; border-bottom: 2px solid var(--brand); }
.sq-quota { display: flex; align-items: center; gap: 10px; font-variant-numeric: tabular-nums; }
.sq-quota progress, .sq-table progress { accent-color: var(--brand); }
.sq-quota progress { width: 120px; height: 8px; }
.sq-btn-main { margin-left: auto; padding: 0 24px; }
.sq-btn-sm { min-height: 34px; padding: 0 20px; font-size: 13px; }
.sq-meta { font-size: 14px; color: var(--muted); }
.sq-saved { display: flex; align-items: baseline; gap: 8px; margin-left: auto; font-variant-numeric: tabular-nums; }
.sq-saved small { font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }
.sq-saved b { font-size: 24px; }
.sq-back-tight { margin-left: 0 !important; }
.sq-fields-audit { grid-template-columns: minmax(0, 2fr) minmax(0, 1.2fr) minmax(0, 2fr); }
.sq-back { margin-left: auto; font-size: 13px; color: var(--muted); }
.sq-fields { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1.2fr) minmax(0, 1fr); gap: 16px; margin: 0 0 20px; align-items: start; }
.sq-fields select, .sq-fields input { width: 100%; }
.sq-fields label { min-width: 0; }
.sq-finding { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); }
.sq-finding[hidden] { display: none; }
.sq-field { margin: 0 0 20px; }
.sq-label { display: block; margin: 0 0 6px; font-size: 14px; font-weight: 600; }
.sq-seg { display: flex; }
.sq-seg label { flex: 1; text-align: center; }
.sq-seg span { line-height: 44px; font-size: 14px; }
.sq-seg .sq-clear input:checked + span { background: #1f7a3d; }
.sq-seg .sq-bad input:checked + span { background: #b3261e; }
#sq-cctv button.sq-save { width: auto; height: var(--tap); padding: 0 40px; margin: 0 0 8px; }
.sq-sub { margin: 28px 0 8px; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); }
.sq-checks { margin: 0; }
.sq-checks td { vertical-align: middle; }
.sq-time { width: 64px; font-variant-numeric: tabular-nums; }
.sq-note { color: var(--muted); }
.sq-act { width: 1%; text-align: right; white-space: nowrap; }
.sq-act form { margin: 0; }
.sq-link { border: 0; background: none; cursor: pointer; color: var(--muted); min-height: 0; width: auto; padding: 0; margin: 0; font: inherit; font-size: 13px; text-decoration: underline; }
.sq-link:hover { color: #b3261e; }
.sq-side { position: sticky; top: 16px; }
.sq-board { margin: 0; }
.sq-board tbody tr { position: relative; }
.sq-board a { color: var(--ink); text-decoration: none; }
.sq-board a::after { content: ""; position: absolute; inset: 0; }
.sq-board tbody tr:hover { background: var(--line-soft); }
.sq-board tr.on { background: var(--line-soft); }
.sq-board tr.on a { font-weight: 700; }
.sq-board td.num, .sq-board th.num { font-variant-numeric: tabular-nums; }
@media (max-width: 720px) {
  .sq-item { grid-template-columns: 1fr; }
  .sq-input { grid-template-columns: 1fr; gap: 32px; }
  .sq-planbar { gap: 4px 16px; }
  .sq-fields { grid-template-columns: 1fr 1fr; }
  .sq-fields label:first-child { grid-column: 1 / -1; }
  .sq-finding { grid-template-columns: 1fr; }
  .sq-side { position: static; }
}
.sq-scale .sq-na span { width: 48px; font-size: 12px; color: var(--muted); }
.sq-scale .sq-na input:checked + span { color: #fff; }

/* --- Date, month and range pickers (bo-date.js; Kelola's PrimeVue Calendar,
   measured on staging 2026-10-15): a filled field with a calendar icon, and a
   306px popup — header ‹ Oktober 2026 ›, 7 columns of 42×34 cells holding a
   round 30×24 day; months and years in 3 columns. ------------------------- */
.bo-date { position: relative; display: inline-flex; align-items: center; min-width: 0; }
.bo-date-input {
  width: 100%; height: var(--bo-control-h); box-sizing: border-box; margin: 0;
  padding: 9px 30px 9px 9px; border: 0; border-radius: var(--bo-radius);
  background: var(--bo-surface-hover); color: var(--bo-text);
  font: 400 var(--bo-fs-base)/var(--bo-lh) var(--bo-font); cursor: pointer; text-overflow: ellipsis;
}
.bo-date-input::placeholder { color: var(--bo-text-muted); }
.bo-date-input:focus-visible, .bo-date.open .bo-date-input { outline: 0; box-shadow: var(--bo-focus); }
.bo-date-input:disabled { cursor: default; opacity: .6; }
.bo-date-icon { position: absolute; right: 9px; width: 14px; height: 14px; pointer-events: none; color: var(--bo-text-muted); }
.bo-date-icon svg { width: 100%; height: 100%; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.bo-flabel .bo-date, .bo-range1 + .bo-date, .bo-rangefield .bo-date { width: 100%; }

.bo-dp {
  position: fixed; z-index: 1200; width: 306px; box-sizing: border-box; padding: 6px;
  background: var(--bo-surface); border-radius: var(--bo-radius); box-shadow: var(--bo-shadow-pop);
  font: 400 var(--bo-fs-base)/var(--bo-lh) var(--bo-font); color: var(--bo-text);
}
.bo-dp-head { display: flex; align-items: center; justify-content: space-between; padding: 6px; font-weight: 600; }
.bo-dp-titles { display: flex; gap: 6px; }
.bo-dp button { font: inherit; color: inherit; background: none; border: 0; margin: 0; cursor: pointer; }
.bo-dp-title { padding: 2px 6px; border-radius: var(--bo-radius); font-weight: 600; }
.bo-dp-title:not(:disabled):hover, .bo-dp-nav:hover { background: var(--bo-surface-hover); }
.bo-dp-title:disabled { cursor: default; }
.bo-dp-nav { width: 24px; height: 24px; padding: 4px; border-radius: 50%; color: var(--bo-text-muted) !important; }
.bo-dp-nav svg { width: 100%; height: 100%; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; display: block; }
.bo-dp-body { display: grid; padding-top: 2px; }
.bo-dp-body.day { grid-template-columns: repeat(7, 1fr); }
.bo-dp-body.month, .bo-dp-body.year { grid-template-columns: repeat(3, 1fr); gap: 0; }
.bo-dp-dow { height: 24px; display: grid; place-items: center; font-weight: 700; }
.bo-dp-cell { display: grid; place-items: center; height: 34px; padding: 0 !important; }
.bo-dp-body.day .bo-dp-cell::before { content: none; }
.bo-dp-body.day .bo-dp-cell { position: relative; z-index: 0; }
.bo-dp-body.day .bo-dp-cell::after {
  content: ""; position: absolute; z-index: -1; width: 30px; height: 24px; border-radius: 50%;
}
.bo-dp-body.month .bo-dp-cell, .bo-dp-body.year .bo-dp-cell { height: 34px; margin: 2px; border-radius: var(--bo-radius); }
.bo-dp-cell:hover:not(:disabled)::after, .bo-dp-body:not(.day) .bo-dp-cell:hover:not(:disabled) { background: var(--bo-surface-hover); }
.bo-dp-cell.on { font-weight: 600; color: var(--bo-primary-pressed) !important; }
.bo-dp-body.day .bo-dp-cell.on::after, .bo-dp-body:not(.day) .bo-dp-cell.on { background: var(--bo-primary-100); }
.bo-dp-cell:disabled { color: var(--bo-text-faint) !important; cursor: default; }
.bo-dp-cell:focus-visible { outline: 0; }
.bo-dp-body.day .bo-dp-cell:focus-visible::after, .bo-dp-body:not(.day) .bo-dp-cell:focus-visible { box-shadow: var(--bo-focus); }

/* Unit Usaha picker (S1): COM chip blue, OUT teal, 12px/600 with 3px 6px padding,
   then the name and the legal name in grey after a dash (measured 2026-10-15). */
.bo-multi-list .bo-chip.k-com, .bo-multi-value .bo-chip.k-com,
.bo-multi-list .bo-chip.k-out, .bo-multi-value .bo-chip.k-out {
  padding: 3px 6px; margin-right: 6px; border-radius: var(--bo-radius);
  font-size: var(--bo-fs-base); font-weight: 600;
  background: var(--bo-chip-bg); color: var(--bo-chip-fg);
}
.bo-multi-list .bo-chip.k-out, .bo-multi-value .bo-chip.k-out { background: var(--bo-chip-out-bg); color: var(--bo-chip-out-fg); }
.bo-multi-sub { margin-left: 3px; color: var(--bo-text-faint); }

/* --- Feedback (bo-ui.js), measured on Kelola staging 2026-10-15 ---------------
   Toast: top-right, 300px, zinc-100 card, round 12px, logo + "Berhasil" + text.
   Error banner: the .bo-flash.bad strip gets a ⊗ icon and a round × close.
   Confirm: 454px dialog, 18px title, rules under the title and above the
   buttons, "Nama : …" rows, Tidak (text) and Ya (primary) on the right.
   Required: 10px red text 3px under the field, a pink border on the field. */
.bo-toasts { position: fixed; z-index: 1300; top: 64px; right: 20px; display: grid; gap: 12px; width: 300px; }
.bo-toast {
  display: flex; align-items: flex-start; gap: 12px; padding: 18px; border-radius: 12px;
  background: var(--bo-surface-hover); color: var(--bo-text-nav); box-shadow: var(--bo-shadow-2);
  font: 400 var(--bo-fs-base)/var(--bo-lh) var(--bo-font); transition: opacity .2s;
}
.bo-toast.out { opacity: 0; }
.bo-toast-logo { width: 34px; height: 34px; object-fit: contain; flex: none; }
.bo-toast-body { flex: 1; display: grid; gap: 2px; }
.bo-toast-body strong { font-weight: 700; color: var(--bo-text); }
.bo-x-close {
  flex: none; display: grid; place-items: center; width: 24px; height: 24px; padding: 4px; margin: 0;
  border: 0; border-radius: 50%; background: none; color: inherit; cursor: pointer;
}
.bo-x-close:hover { background: rgba(0, 0, 0, .06); }
.bo-x-close svg, .bo-flash-icon svg { width: 100%; height: 100%; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
.bo-flash.bo-flash-x { display: flex; align-items: center; gap: 6px; font-weight: 600; }
.bo-flash-x .bo-flash-icon { flex: none; width: 18px; height: 18px; }
.bo-flash-x .bo-x-close { margin-left: auto; }

.bo-ask {
  width: 454px; max-width: calc(100vw - 32px); padding: 0; border: 0; border-radius: var(--bo-radius);
  box-shadow: var(--bo-shadow-dialog); background: var(--bo-surface); color: var(--bo-text);
  font: 400 var(--bo-fs-base)/var(--bo-lh) var(--bo-font);
}
.bo-ask::backdrop { background: var(--bo-mask); }
.bo-ask h2 {
  margin: 0; padding: var(--bo-dialog-pad); border-bottom: 1px solid var(--bo-border);
  font: 600 var(--bo-fs-dialog)/1.4 var(--bo-font); color: var(--bo-text);
}
.bo-ask-body { padding: var(--bo-dialog-pad); }
.bo-ask-body p { margin: 0; font-weight: 500; color: var(--bo-text-strong); }
.bo-ask-body dl { display: grid; grid-template-columns: 100px 1fr; gap: 6px 0; margin: 12px 0 0; }
.bo-ask-body dt { color: var(--bo-text-muted); font-weight: 500; }
.bo-ask-body dd { margin: 0; color: var(--bo-text); }
.bo-ask footer {
  display: flex; justify-content: flex-end; gap: var(--bo-inline-gap);
  padding: 12px var(--bo-dialog-pad) var(--bo-dialog-pad); border-top: 1px solid var(--bo-border);
}
.bo-ask footer button { min-width: 72px; height: var(--bo-control-h); }
.bo-ask-no {
  padding: 9px 12px; border: 1px solid transparent; border-radius: var(--bo-radius); background: none;
  color: var(--bo-primary); font: 700 var(--bo-fs-base) var(--bo-font); cursor: pointer;
}
.bo-ask-no:hover { background: var(--bo-primary-50); }
.bo-ask-no:focus-visible { outline: 0; box-shadow: var(--bo-focus); }

.bo-req { color: var(--bo-required); }
.bo-err { display: block; margin-top: 3px; font: 400 10px/1.4 var(--bo-font); color: var(--bo-invalid); }
.bo-invalid, input.bo-invalid, .bo-multi-btn.bo-invalid { border-color: var(--bo-invalid-border) !important; box-shadow: 0 0 0 1px var(--bo-invalid-border); }

/* Upload Jurnal (Kelola's two cards, 2026-10-15): template card left, status card right. */
.bo-upload-page { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; align-items: start; margin-top: 18px; }
@media (max-width: 1000px) { .bo-upload-page { grid-template-columns: 1fr; } }
.bo-upload-page .bo-card { margin: 0; padding: 24px; background: var(--bo-surface); border: 0; }
.bo-card-title { display: flex; align-items: center; gap: 9px; margin: 0 0 18px !important; font-size: 15px !important; font-weight: 700 !important; color: var(--bo-text) !important; }
.bo-card-title svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.bo-ic-doc { color: var(--bo-info); }
.bo-ic-clock { color: var(--bo-warning); }
.bo-note-info {
  display: flex; align-items: center; gap: 8px; margin: 0 0 12px; padding: 14px 12px;
  border: 1px solid var(--bo-info); border-radius: var(--bo-radius); font: 400 var(--bo-fs-base)/1.5 var(--bo-font); color: var(--bo-text-strong);
}
.bo-note-info svg { flex: none; width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
.bo-primary.bo-block { display: flex; width: 100%; box-sizing: border-box; justify-content: center; position: relative; }
.bo-primary.bo-block svg { position: absolute; left: 12px; }
.bo-rule-line { margin: 12px 0 0; border: 0; border-top: 1px solid var(--bo-border); }
.bo-upload-status { display: grid; gap: 12px; padding: 12px; border: 1px solid var(--bo-border); border-radius: var(--bo-radius); font-family: var(--bo-font); font-size: var(--bo-fs-base); }
.bo-counters { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
.bo-counters div { display: grid; justify-items: center; gap: 4px; padding: 9px; border-radius: 6px; background: var(--bo-ground); }
.bo-counters b { font-size: 18px; font-weight: 700; color: var(--bo-text-strong); }
.bo-counters span { font-size: 9.6px; color: var(--bo-text-muted); }
.bo-counters .ok span { color: var(--bo-success-text); }
.bo-counters .bad span { color: var(--bo-danger); }
/* Kelola's "Kesalahan Dalam Pengajuan Transaksi" panel: the error strip holding a Baris | Deskripsi table. */
.bo-flash.bo-errtable { display: grid; gap: 8px; margin-top: 12px; }
.bo-errtable table { width: 100%; border-collapse: collapse; background: var(--bo-surface); color: var(--bo-text); font-weight: 400; }
.bo-errtable th, .bo-errtable td { padding: 6px 9px; text-align: left; border-bottom: 1px solid var(--bo-border-row); }
.bo-errtable th { font-weight: 700; color: var(--bo-text-strong); border-bottom: 2px solid var(--bo-border-table); }
.bo-errtable th:first-child, .bo-errtable td:first-child { width: 80px; }

/* Document lines, Kelola's one-line editor (bo-doc.js, staging 2026-10-15): lines read as text; the
   open line is tinted and shows its inputs; the trash becomes a round primary save (disk) button;
   a new line shows a red ✕ instead. Without JS (no .bo-js) a draft shows every input. */
form[data-bo-lines]:not(.bo-js) tr.bo-editable .bo-view { display: none; }
form.bo-js tr.bo-editable:not(.editing) .bo-edit { display: none !important; }
form.bo-js tr.editing .bo-view { display: none; }
form.bo-js tr.bo-editable { cursor: pointer; }
form.bo-js tr.editing { cursor: default; }
form.bo-js tr.editing > td { background: var(--bo-row-hover); }
.bo-line-save, .bo-line-cancel {
  display: none; place-items: center; width: 24px; height: 24px; min-height: 0; margin: 0; padding: 4px;
  border: 0; border-radius: 50%; color: var(--bo-on-primary); cursor: pointer;
}
.bo-line-save { background: var(--bo-primary); }
.bo-line-cancel { display: grid; background: var(--bo-danger); }
.bo-line-save svg, .bo-line-cancel svg { width: 100%; height: 100%; fill: none; stroke: currentColor; stroke-width: 2; }
form.bo-js tr.editing .bo-line-save { display: grid; }
form.bo-js tr.editing:not([data-new]) .bo-trash { display: none; }
form:not(.bo-js) .bo-line-save { display: none; }
.bo-pen { display: inline-block; width: 13px; height: 13px; margin-right: 6px; vertical-align: -1px; color: var(--bo-text-muted); }
.bo-pen svg { width: 100%; height: 100%; fill: none; stroke: currentColor; stroke-width: 1.8; }
tr.bo-editable:not(:hover) .bo-pen { visibility: hidden; }
.bo-add-item { margin: 6px 0 0; }
.bo-add-btn {
  display: flex; align-items: center; justify-content: center; gap: 8px; width: calc(36% - 6px); height: 30px; margin: 0 0 0 7%;
  border: 0; border-radius: var(--bo-radius); background: var(--bo-surface-hover); color: var(--bo-success-text);
  font: 500 var(--bo-fs-base) var(--bo-font); cursor: pointer;
}
.bo-add-btn:hover { background: var(--bo-field-bg-hover); }
.bo-add-btn svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; }

/* Status menu chips (Kelola): HAPUS black, SETUJUI green, BATAL PERSETUJUAN red, BATAL black, then the plain
   "Lihat alur dokumen" link under a rule. */
.bo-status-pop { display: grid; justify-items: start; gap: 8px; min-width: 150px; padding: 9px 12px; }
.bo-status-pop .bo-act {
  height: auto; min-height: 0; margin: 0; padding: 3px 12px; border: 0; border-radius: var(--bo-radius-sm);
  color: var(--bo-on-primary); font: 700 var(--bo-fs-base) var(--bo-font); cursor: pointer;
}
.bo-act.k-delete, .bo-act.k-void { background: #000; }
.bo-act.k-approve { background: #15803d; }
.bo-act.k-unapprove { background: var(--bo-danger); }
.bo-act-flow {
  justify-self: stretch; margin: 2px -12px 0; padding: 9px 12px 0; border: 0; border-top: 1px solid var(--bo-border);
  background: none; color: var(--bo-text); font: 400 var(--bo-fs-base) var(--bo-font); text-align: left; cursor: pointer;
}
.bo-flow-list { margin: 0; padding-left: 18px; display: grid; gap: 8px; }
.bo-flow-list li { display: grid; gap: 2px; }
details.bo-errtable > summary { list-style: none; cursor: pointer; }
details.bo-errtable > summary::-webkit-details-marker { display: none; }
.bo-linkbtn { justify-self: start; margin: 0; padding: 0; border: 0; background: none; color: var(--bo-link); font: 600 var(--bo-fs-base) var(--bo-font); cursor: pointer; }
.bo-status-pop .bo-act { width: auto; text-align: center; }
.bo-pop .bo-menu-item { display: flex; align-items: center; gap: 8px; }
.bo-pop .bo-menu-item svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.8; }

/* Ganti Kata Sandi (Kelola's change-password card, 2026-10-15): centred, 474px fields with an eye. */
.bo-pw-card {
  display: grid; gap: 14px; width: min(504px, 100%); box-sizing: border-box; margin: 60px auto 0; padding: 24px 15px;
  background: var(--bo-surface); border-radius: var(--bo-radius); box-shadow: var(--bo-shadow-card); font-family: var(--bo-font);
}
.bo-pw-card h1 { margin: 0 0 6px; text-align: center; font-size: var(--bo-fs-dialog); font-weight: 600; color: var(--bo-text); }
.bo-pw-field { display: grid; gap: 6px; font-size: var(--bo-fs-base); font-weight: 600; color: var(--bo-text-strong); }
.bo-pw-box { position: relative; display: block; }
.bo-pw-box input {
  width: 100%; height: var(--bo-control-h); box-sizing: border-box; margin: 0; padding: 9px 34px 9px 9px;
  border: 1px solid #d4d4d8; border-radius: var(--bo-radius); background: var(--bo-surface); font: 400 var(--bo-fs-base) var(--bo-font);
}
.bo-pw-eye { position: absolute; right: 6px; top: 5px; width: 24px; height: 24px; min-height: 0; padding: 4px; margin: 0; border: 0; background: none; color: var(--bo-text-muted); cursor: pointer; }
.bo-pw-eye svg, .bo-pw-done svg { width: 100%; height: 100%; fill: none; stroke: currentColor; stroke-width: 1.8; }
.bo-pw-forgot { justify-self: start; color: var(--bo-link); font-size: var(--bo-fs-base); font-weight: 600; cursor: help; }
.bo-pw-done { position: relative; display: flex; justify-content: center; align-items: center; height: var(--bo-control-h); color: var(--bo-text); font-size: var(--bo-fs-base); text-decoration: none; }
.bo-pw-done svg { position: absolute; left: 12px; width: 14px; height: 14px; }

/* Tutup Buku as Kelola's /transaction/closing-book (measured on staging 2026-10-15). */
.bo-legend-box { margin: 18px 0; padding: 4.2px 9px 7.5px; border: 0.8px solid var(--bo-border); border-radius: var(--bo-radius);
  background: var(--bo-surface); font-family: var(--bo-font); font-size: var(--bo-fs-base); color: var(--bo-text); }
.bo-legend-box legend { padding: 6px 15px; border: 0.8px solid var(--bo-border); border-radius: var(--bo-radius);
  background: var(--bo-ground); font-weight: 700; }
.bo-legend-box p { margin: 12px 0; }
.bo-close-filter { display: flex; align-items: flex-start; gap: 18px; margin-bottom: 12px; }
.bo-close-filter .bo-field > span:first-child { font-weight: 500; color: var(--bo-text-strong); margin-bottom: 6px; }
.bo-close-filter .bo-field:first-child { width: 390px; }
.bo-close-year select { width: 99px; height: 34px; }
.bo-close-start { width: 286px; }
.bo-close-start input { height: 34px; padding: 0 9px; border: 0.8px solid transparent; border-radius: var(--bo-radius);
  background: var(--bo-surface-hover); color: var(--bo-text); font: inherit; }
.bo-close-start small { margin-top: 6px; font-size: 10px; color: var(--bo-text); }
.bo-close-start small a { color: var(--bo-link); }
.bo-close-table { width: 100%; }
.bo-close-table th, .bo-close-table td { text-align: center; vertical-align: middle; }
.bo-close-table td:first-child { width: 307px; }
.bo-close-btn { width: 295px; height: 34px; border-radius: var(--bo-radius); font: inherit; font-size: 12px; cursor: pointer; }
.bo-close-btn.go { border: 0.8px solid var(--bo-info); background: var(--bo-info); color: #fff; }
.bo-close-btn.undo { border: 0.8px solid var(--bo-danger); background: transparent; color: var(--bo-danger); }
.bo-close-btn:disabled { opacity: .6; cursor: not-allowed; }
.bo-close-pill { display: inline-block; width: 215px; padding: 6px; border-radius: var(--bo-radius); font-weight: 700; line-height: 18px; }
.bo-close-pill.mig { background: #faedc4; color: #c79807; }
.bo-close-pill.done { background: #caf1d8; color: #1da750; }
.bo-close-pill.open { background: #dadee3; color: #52525b; }
.bo-close-last { text-align: left !important; width: 190px; }
.bo-close-last > * { display: block; max-width: 154px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.bo-close-last small { color: var(--bo-text-muted); }
.bo-close-report summary { display: flex; align-items: center; justify-content: space-between; width: 234px; height: 34px;
  padding: 0 9px; color: #64748b; font-weight: 700; cursor: pointer; list-style: none; }
.bo-close-report summary svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; }
@media (max-width: 900px) {
  .bo-close-filter { flex-wrap: wrap; }
  .bo-close-filter .bo-field:first-child, .bo-close-start { width: 100%; }
  .bo-close-table { display: block; overflow-x: auto; }
}

/* Kelola's tiered "..." menu (Pemasok / Pelanggan): a row with › opens its submenu to the left. */
.bo-tier { min-width: 170px; }
.bo-tier > details { position: relative; }
.bo-tier > details > summary { display: flex; justify-content: space-between; gap: 12px; padding: 8px 14px; cursor: pointer; list-style: none; }
.bo-tier > details > summary::-webkit-details-marker { display: none; }
.bo-tier > details > summary::after { content: "›"; color: var(--bo-text-muted); }
.bo-tier > details > summary:hover { background: var(--bo-surface-hover); color: var(--bo-primary); }
.bo-tier > details > .bo-pop { position: absolute; top: 0; right: 100%; min-width: 120px; }
.bo-tier .bo-pop > a { display: block; }

/* Small two-column dialog (Tambah Produk, 423px in Kelola) and the "IDR" addon before a money input. */
.bo-dialog.bo-dialog-sm { width: min(423px, calc(100vw - 32px)); }
.bo-addon { display: flex; }
.bo-addon i { display: grid; place-items: center; padding: 0 9px; border: 2px solid var(--bo-border-input); border-right: 0;
  border-radius: var(--bo-radius) 0 0 var(--bo-radius); background: var(--bo-surface-hover); font-style: normal; font-weight: 400; color: var(--bo-text-muted); }
.bo-dialog-body .bo-addon input:not([type=radio]):not([type=checkbox]) { border-radius: 0 var(--bo-radius) var(--bo-radius) 0; text-align: right; }

/* Kelola's full-page master form (Aset Tetap › Tambah): two 544px columns, Simpan + Kembali on the right. */
.bo-form.bo-form2 { grid-template-columns: repeat(2, minmax(0, 544px)); gap: 18px 12px; max-width: none; }
.bo-form2 input:not([type=checkbox]):not([type=radio]), .bo-form2 select { height: 34px; box-sizing: border-box; width: 100%; }
.bo-form2 .bo-addon input { border-radius: 0 var(--bo-radius) var(--bo-radius) 0; }
.bo-qty { display: grid; grid-template-columns: 336fr 209fr; }
.bo-form-foot { display: flex; justify-content: flex-end; gap: 6px; }
.bo-form-foot > * { min-width: 96px; justify-content: center; }
@media (max-width: 900px) { .bo-form.bo-form2 { grid-template-columns: 1fr; } .bo-form2 > span:empty { display: none; } }

/* Header Cari panel and Notifikasi panel, as Kelola's (measured 2026-10-15). */
.bo-search { position: relative; }
.bo-find-pop { position: absolute; top: calc(100% + 8px); left: 0; z-index: 30; width: 336px; max-height: 420px; overflow-y: auto;
  padding: 12px; box-sizing: border-box; background: var(--bo-surface); border-radius: var(--bo-radius);
  box-shadow: 0 20px 25px -5px rgba(0,0,0,.1), 0 8px 10px -6px rgba(0,0,0,.1); color: var(--bo-text); cursor: default; }
.bo-find-pop h3 { margin: 6px 0 8px; font-size: 13.5px; font-weight: 700; }
.bo-find-hint { margin: 4px 0; color: var(--bo-text-faint); }
.bo-find-card, .bo-notif-item { display: flex; gap: 12px; align-items: flex-start; margin-bottom: 8px; padding: 9px; border-radius: var(--bo-radius);
  box-shadow: 0 1px 3px rgba(0,0,0,.1); color: var(--bo-text); text-decoration: none; }
.bo-find-card:hover { background: var(--bo-surface-hover); }
.bo-find-ic { display: grid; place-items: center; flex: none; width: 36px; height: 36px; border-radius: var(--bo-radius); background: #dbeafe; color: var(--bo-info); }
.bo-find-ic svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.6; }
.bo-find-body { display: grid; gap: 2px; flex: 1; min-width: 0; }
.bo-find-body strong { font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bo-find-sub { color: var(--bo-text-faint); font-weight: 500; }
.bo-find-desc { color: var(--bo-text-faint); }
.bo-find-tag { flex: none; font-size: 10.5px; font-weight: 700; color: var(--bo-text-faint); }
.bo-find-more { color: var(--bo-link); text-decoration: none; }
.bo-icon { position: relative; }
.bo-count { position: absolute; top: -6px; right: -8px; min-width: 18px; padding: 1px 4px; box-sizing: border-box; border-radius: 9px;
  background: var(--bo-danger); color: #fff; font-size: 9px; font-weight: 700; line-height: 14px; text-align: center; }
.bo-notif-pop { width: 591px; max-width: calc(100vw - 32px); max-height: 420px; overflow-y: auto; padding: 12px 15px; box-sizing: border-box; }
.bo-notif-pop h2 { margin: 0 0 9px; font-size: 18px; font-weight: 600; color: var(--bo-text-faint); }
.bo-notif-tabs { display: flex; gap: 18px; margin-bottom: 9px; border-bottom: 1px solid var(--bo-border); }
.bo-notif-pop .bo-notif-tabs button { width: auto; padding: 6px 2px; border-bottom: 2px solid transparent; color: var(--bo-text-muted); }
.bo-notif-pop .bo-notif-tabs button.on { border-bottom-color: var(--bo-primary); color: var(--bo-primary); }
.bo-notif-item.unread strong::after { content: " •"; color: var(--bo-danger); }
@media (max-width: 600px) { .bo-find-pop { width: calc(100vw - 32px); } }

/* Saldo Awal Trial Balance (Kelola /transaction/initial-trial-balance). */
.bo-opening-bar select[name=unit] + .bo-multi, .bo-opening-bar .bo-multi { width: 298px; }
.bo-opening-bar input[type=month] { width: 196px; }
.bo-opening { width: 100%; }
.bo-opening td { vertical-align: middle; }
.bo-opening .bo-acode { min-width: 68px; justify-content: center; font-weight: 700; color: var(--bo-chip-fg); }
.bo-opening td.num input { width: 126px; height: 34px; box-sizing: border-box; text-align: right; }
/* bo-rowedit.js: figures read as text until the row's pencil opens them (Saldo Awal, CA02 prices). */
table[data-bo-rowedit].bo-js [data-bo-cell] { display: none; }
table[data-bo-rowedit].bo-js tr.bo-open [data-bo-cell] { display: inline-block; }
table[data-bo-rowedit].bo-js tr.bo-open .bo-val, table[data-bo-rowedit]:not(.bo-js) .bo-val, table[data-bo-rowedit]:not(.bo-js) .bo-rowpen { display: none; }
.bo-rowpen svg { width: 16px; height: 16px; fill: none; stroke: var(--bo-primary); stroke-width: 1.8; }
.bo-rowpen .i-disk, tr.bo-open .bo-rowpen .i-pen { display: none; }
tr.bo-open .bo-rowpen .i-disk { display: inline; }
.bo-close-filter .bo-grow { flex: 1; }
.bo-close-filter .bo-multi { width: 100%; }
.bo-close-filter .bo-multi.has-value .bo-multi-label { display: none; } /* Kelola labels this field above, not on its border */
.bo-close-year { width: 99px; }
.bo-rowpen { padding: 6px; border: 0; background: none; cursor: pointer; }
.bo-opening th:first-child, .bo-opening td:first-child, .bo-opening td:nth-child(3), .bo-opening th:nth-child(3) { text-align: left; }
.bo-opening td:nth-child(2) { text-align: center; }
.bo-opening .bo-badge.dc { min-width: 87px; }
.bo-rowpen:hover, .bo-rowpen:focus { background: var(--bo-surface-hover); border-radius: var(--bo-radius); box-shadow: none; }
.bo-find-pop, .bo-notif-pop { font-family: var(--bo-font); font-size: 12px; font-weight: 400; line-height: 1.5; text-align: left; }
.bo-lines select.bo-sidesel { width: 100px; height: var(--bo-control-h); padding: 0 9px; border: 2px solid var(--bo-field-bg); border-radius: var(--bo-radius);
  background-color: var(--bo-field-bg); color: var(--bo-text); font-weight: 400; cursor: pointer; }
/* Stok Opname: product left with its code under it, compact count input. */
.bo-opname { width: 100%; }
.bo-opname th:first-child, .bo-opname td:first-child { text-align: left; }
.bo-opname td:first-child small { display: block; }
.bo-opname td.num input { width: 126px; height: 34px; box-sizing: border-box; text-align: right; }
.bo-form2 input:not([type=checkbox]):not([type=radio]):not(.bo-drop-input), .bo-form2 select {
  min-height: 0; padding: 0 9px; border: 2px solid var(--bo-border-input); border-radius: var(--bo-radius);
  font-family: var(--bo-font); font-size: var(--bo-fs-base); color: var(--bo-text); }
.bo-form2 .bo-addon { height: 34px; }
.bo-form2 .bo-addon i { box-sizing: border-box; }
.bo-opname td.num input { min-height: 0; padding: 0 9px; }
.bo-form-foot > * { flex: none; width: auto; }
.bo-form2 select { height: 34px; line-height: 30px; }
.bo-form2 .bo-multi.has-value .bo-multi-label { display: none; }
.bo-dialog-body textarea { box-sizing: border-box; width: 100%; padding: 6px 9px; border: 2px solid var(--bo-border-input); border-radius: var(--bo-radius); font: inherit; font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 400; color: var(--bo-text); resize: vertical; }
.bo-sub2 { display: block; }
/* CA02 draft: the pencil on a product line's Jumlah opens its invoice price. */
.bo-price-edit { display: inline-flex; align-items: center; gap: 4px; margin-left: 4px; }
.bo-price-edit .bo-val { display: none !important; }
.bo-price-edit input { width: 110px; height: 30px; box-sizing: border-box; text-align: right; }
/* Kelola's p-accordion on Tambah Aset Tetap: grey header bar with a chevron, white body, one outline around the stack. */
.bo-acc > summary {
  display: flex; align-items: center; gap: 6px; padding: 15px; list-style: none; cursor: pointer;
  background: var(--bo-ground); border: 0.8px solid var(--bo-border);
  font-family: var(--bo-font); font-size: var(--bo-fs-base); font-weight: 700; line-height: 1.1; color: var(--bo-text);
}
.bo-acc > summary::-webkit-details-marker { display: none; }
.bo-acc > summary::before {
  content: ""; flex: none; width: 5px; height: 5px; margin: 0 4px 2px 2px;
  border: solid currentColor; border-width: 0 1.5px 1.5px 0; transform: rotate(-45deg);
}
.bo-acc[open] > summary::before { transform: rotate(45deg); }
.bo-acc > .bo-form { align-content: start; padding: 15px; border: 0.8px solid var(--bo-border); border-top: 0; }
.bo-acc:first-of-type > summary { border-radius: var(--bo-radius) var(--bo-radius) 0 0; }
.bo-acc:last-of-type:not([open]) > summary { border-radius: 0 0 var(--bo-radius) var(--bo-radius); }
.bo-acc + .bo-acc { margin-top: -18px; }
.bo-form.bo-form2:has(> .bo-acc) { grid-template-columns: minmax(0, 1fr); }
.bo-acc > .bo-form.bo-form2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* Kelola's field rhythm in the accordion: 24px label line, 6px, 34px control, 18px to the next row (82px). */
.bo-acc > .bo-form > label { align-content: start; gap: 6px; margin: 0; }
.bo-acc > .bo-form input { margin: 0; }
.bo-acc > .bo-form > label > span:first-child { line-height: 24px; }
.bo-acc + .bo-acc > summary { border-top: 0; }
/* Disposisi dialog: Kelola's "Informasi aset" block, label : value like the Konfirmasi rows. */
.bo-asset-info {
  display: grid; grid-template-columns: max-content 1fr; gap: 4px 12px; margin: 0; padding: 12px 15px;
  background: var(--bo-ground); border: 0.8px solid var(--bo-border); border-radius: var(--bo-radius);
  font-family: var(--bo-font); font-size: var(--bo-fs-base);
}
.bo-asset-info dt { color: var(--bo-text-muted); }
.bo-asset-info dt.bo-wide { color: var(--bo-text); font-weight: 700; margin-bottom: 4px; }
.bo-asset-info dd { margin: 0; color: var(--bo-text); }
.bo-asset-info dd::before { content: ": "; }
/* Dialog check rows (Kirim Informasi ke E-Mail Pengguna): box and text on one line, as in Kelola. */
.bo-dialog-body label.bo-check { display: flex; align-items: center; gap: 8px; }
.bo-linkrow .bo-outline { justify-self: start; }

/* Penjualan Offline: a lost number, its write-off note and button on one row. */
.bo-gapform { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; justify-content: center; margin: 2px 0; }
.bo-gapform input { box-sizing: border-box; height: var(--bo-control-h); padding: 0 9px; border: 1px solid var(--bo-field-bg); border-radius: var(--bo-radius); background: var(--bo-field-bg); font: inherit; font-size: var(--bo-fs-base); }
/* Kelola's blue history badge (Pengguna › Detail Login count) and history-icon button (Penomoran › Nomor Terakhir). */
.bo-hist { display: inline-block; min-width: 22px; margin-left: 8px; padding: 1px 7px; border-radius: 999px; background: var(--bo-chip-bg); color: var(--bo-chip-fg); font-size: 12px; font-weight: 700; text-align: center; }
.bo-histbtn { display: inline-block; }
.bo-histicon { display: grid; place-items: center; width: 32px; height: 32px; margin: 0 auto; border-radius: var(--bo-radius); background: var(--bo-chip-bg); color: var(--bo-chip-fg); cursor: pointer; list-style: none; }
.bo-histicon::-webkit-details-marker { display: none; }
.bo-histicon svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.bo-histicon:hover { filter: brightness(.95); }
.bo-addon output { flex: 1; display: block; padding: 8px 10px; border: 2px solid var(--bo-border-input); border-left-color: var(--bo-border-input); border-radius: 0 var(--bo-radius) var(--bo-radius) 0; background: var(--bo-field-bg); text-align: right; font-weight: 700; }
.bo-addon output.bad { color: var(--bo-danger); }
/* Buat Transaksi: "Edit Format?" sits at the right end of the No. Dokumen label. */
.bo-create-dlg label:has(> .bo-fmt) { position: relative; }
.bo-create-dlg .bo-fmt { position: absolute; top: 0; right: 0; font-size: var(--bo-fs-sm, 12px); font-weight: 600; color: var(--bo-link); text-decoration: none; }
.bo-create-dlg .bo-fmt:hover { text-decoration: underline; }

/* Phone width (390px): nothing may push the page sideways. */
@media (max-width: 560px) {
  .bo-seg-layout { grid-template-columns: minmax(0, 1fr); }
  .bo-pager { flex-wrap: wrap; padding: 9px 8px; row-gap: 6px; }
  .bo-pager .count { flex-basis: 100%; margin-right: 0; text-align: center; white-space: normal; }
  .bo-dash .bo-filters { flex-wrap: wrap; }
  .bo-dash .bo-filters .bo-field, .bo-dash .bo-filters .bo-multi { width: 100%; max-width: 100%; }
  .bo-widgets { grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); }
  /* 24 bars at ~7px each: label every other one so the digits do not touch. */
  .bo-bars:has(li:nth-child(13)) li:nth-child(even) small { visibility: hidden; }
}

/* Customer Composition (§7 modal): one 100% bar + legend table, built by
   charts.js compositionBlock. Segment colours are inline (they are data). */
.comp-total { margin: 0 0 6px; font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; }
.comp-bar { display: flex; height: 38px; border: 1px solid var(--line); }
.comp-seg {
  display: flex; align-items: center; justify-content: center; min-width: 2px;
  font-size: 13px; font-weight: 650; font-variant-numeric: tabular-nums; overflow: hidden; white-space: nowrap;
}
.comp-legend { border-collapse: collapse; width: 100%; margin-top: 10px; font-size: 13px; font-variant-numeric: tabular-nums; }
.comp-legend td { padding: 6px 10px 6px 0; border-bottom: 1px solid var(--line); vertical-align: baseline; }
.comp-legend tr:last-child td { border-bottom: 0; }
.comp-legend td.num { text-align: right; white-space: nowrap; padding-left: 14px; }
.comp-legend td:first-child { width: 18px; }
.comp-dot { display: inline-block; width: 10px; height: 10px; }
.comp-name { font-weight: 600; white-space: nowrap; }
.comp-note { color: #6b6b6b; }

/* --- HRD: 1 on 1 database ------------------------------------------------ */

/* The search box carries the width; the five scope selects stay a fixed,
   predictable size beside it. */
.oo-filters .oo-search { flex: 1 1 280px; }
.oo-filters .oo-search input { width: 100%; }
.oo-filters .oo-reset { align-self: center; padding: 0 6px; }
.oo-count { margin: -4px 0 12px; }

.users-table.oo-table { table-layout: fixed; min-width: 980px; margin-bottom: 0; }
.users-table.oo-table th:nth-child(1) { width: 11%; }  /* Tanggal */
.users-table.oo-table th:nth-child(2) { width: 18%; }  /* Karyawan */
.users-table.oo-table th:nth-child(3) { width: 17%; }  /* Posisi / Unit */
.users-table.oo-table th:nth-child(4) { width: 16%; }  /* Dilakukan oleh */
.users-table.oo-table th:nth-child(5) { width: 11%; }  /* Topik */
.users-table.oo-table th:nth-child(6) { width: 13%; }  /* Status */
.users-table.oo-table th:nth-child(7) { width: 14%; }  /* Tindak lanjut */
.users-table.oo-table td { vertical-align: top; }
.oo-nowrap { white-space: nowrap; }
.oo-sub { display: block; color: var(--muted); font-size: 12px; margin-top: 2px; }

/* The whole row opens the note; the name is the real link and stretches over
   the row, so it stays one tab stop and middle-click still works. */
.users-table.oo-table tbody tr { position: relative; }
.users-table.oo-table tbody tr:hover { background: #f6f6f6; }
.users-table.oo-table .baris-link { color: var(--ink); font-weight: 600; text-decoration: none; }
.users-table.oo-table .baris-link::after { content: ""; position: absolute; inset: 0; }
.users-table.oo-table tbody tr:hover .baris-link {
  color: var(--brand); text-decoration: underline; text-decoration-thickness: 1px;
}
.users-table.oo-table .baris-link:focus-visible { outline: none; }
.users-table.oo-table tbody tr:has(.baris-link:focus-visible) { outline: 2px solid var(--brand); outline-offset: -2px; }

.oo-empty { padding: 36px 20px; text-align: center; }
.oo-empty strong { display: block; font-size: 15px; margin-bottom: 4px; }
.oo-empty span { display: block; color: var(--muted); font-size: 13px; margin-bottom: 14px; }
.oo-empty .btn { width: auto; }

.oo-badge { display: inline-block; padding: 2px 9px; border: 1px solid; border-radius: 999px;
  font-size: 12px; font-weight: 600; white-space: nowrap; }
.oo-badge.is-selesai { color: #2f5233; background: #eef3ee; border-color: #bcd0bd; }
.oo-badge.is-tindak { color: #8a5a12; background: #fdf3e3; border-color: #e5c99a; }

.oo-pager { display: flex; align-items: center; gap: 10px; margin: 16px 0 28px; }
.oo-pager .btn { min-height: 36px; padding: 0 14px; font-size: 14px; }
.oo-pager .btn.is-off { color: var(--muted); background: #f6f6f6; pointer-events: none; }
.oo-page-of { color: var(--muted); font-size: 13px; }

.oo-subtitle { margin: 4px 0 0; }
.page-actions .btn { width: auto; }

.oo-form { margin: 0 0 28px; }
.oo-card { border: 1px solid var(--line); padding: 18px; margin: 0 0 16px; }
.oo-card h2 { font-size: 13px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--muted); margin: 0 0 14px; }
.oo-card h3 { font-size: 14px; margin: 18px 0 6px; }
.oo-card h3:first-of-type { margin-top: 0; }
.oo-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px 16px; }
.oo-grid label { display: flex; flex-direction: column; gap: 6px; font-size: 13px; }
.oo-grid label > span { color: var(--muted); font-weight: 600; }
.oo-grid input, .oo-grid select, .oo-grid textarea {
  width: 100%; box-sizing: border-box; min-height: var(--tap); padding: 10px 12px;
  font: inherit; font-size: 16px; color: var(--ink); background: var(--field);
  border: 1px solid var(--line); border-radius: 8px;
}
.oo-grid textarea { min-height: 110px; resize: vertical; font-size: 15px; }
.oo-wide { grid-column: 1 / -1; }
.oo-help { color: var(--muted); font-size: 12px; }
.oo-help a { color: var(--brand); font-weight: 600; }
.oo-actions { display: flex; justify-content: flex-end; gap: 10px; }
.oo-actions .btn { width: auto; }

.oo-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 14px 20px; border: 1px solid var(--line); padding: 18px; margin: 0 0 16px; }
.oo-facts dt { color: var(--muted); font-size: 12px; text-transform: uppercase; letter-spacing: .03em; }
.oo-facts dd { margin: 5px 0 0; font-weight: 600; }
.oo-facts-inline { border: 0; padding: 0; margin: 0; }
.oo-note { white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.55; margin: 0; }

@media (max-width: 720px) {
  .oo-filters .oo-search { flex-basis: 100%; }
  .oo-actions { flex-direction: column-reverse; }
  .oo-actions .btn { width: 100%; }
  .oo-pager { flex-wrap: wrap; }
}

/* An open follow-up whose date has passed. The colour is the cue; the date is
   still printed, so nothing depends on being able to distinguish it. */
.oo-late { color: #a3341f; }

/* Database Pelamar lookup (js/pelamar-pick.js): people who already exist, offered while typing. */
.pelamar-hits { list-style: none; margin: 0 0 12px; padding: 0; border: 1px solid var(--line); border-radius: 8px; overflow: hidden; }
.pelamar-hits li + li { border-top: 1px solid var(--line); }
.pelamar-hits button { display: block; width: 100%; padding: 8px 12px; border: 0; background: transparent; color: var(--ink); font: inherit; text-align: left; cursor: pointer; }
.pelamar-hits button:hover, .pelamar-hits button:focus-visible { background: var(--surface-2, rgba(127, 127, 127, 0.12)); }
.pelamar-terpilih { margin: 0 0 12px; }
.btn.buka-pelamar { width: auto; }
.pelamar-hits::before { content: attr(aria-label); display: block; padding: 6px 12px; font-size: 12px; color: var(--muted); background: rgba(127, 127, 127, 0.08); }

/* HRD Attendance › Ringkasan: composition pie of the days due (Kehadiran.Rollup#conic). */
:root { --att-hadir: var(--s6); --att-alpha: var(--s1); --att-cuti: var(--s4); --att-sakit: var(--s3); --att-izin: var(--s5); }
.att-pie { display: flex; align-items: center; gap: 32px; margin: 0 0 20px; flex-wrap: wrap; }
.att-pie-disc { position: relative; width: 176px; height: 176px; border-radius: 50%; flex: none; }
.att-pie-disc::after { content: ""; position: absolute; inset: 34px; border-radius: 50%; background: var(--bg, #fff); }
.att-pie h3 { margin: 0 0 8px; font-size: 14px; }
.att-pie-legend { border-collapse: collapse; font-size: 13px; }
.att-pie-legend td { padding: 4px 12px 4px 0; vertical-align: middle; }
.att-pie-legend i { display: block; width: 10px; height: 10px; border-radius: 2px; }
.att-pie-legend .num { text-align: right; font-variant-numeric: tabular-nums; }
.att-pie-legend .pct { color: var(--muted); padding-right: 0; min-width: 52px; }
.att-pie .hint { margin: 8px 0 0; }

/* --- HRD: Poin & SP ------------------------------------------------------
   Red = points still active, green = lapsed (history), ink = a letter in force. The same three
   colours mean the same thing on Employee Archive, Data Complain, Findings SQ and the report. */
:root { --pt-aktif: #a3301a; --pt-lapse: #1e6b33; --pt-sp: #1a1a1a; }

.pt-badges { display: inline-flex; vertical-align: middle; margin-left: 8px; }
.pt-badges-link { display: inline-flex; gap: 2px; text-decoration: none; }
.pt-chip {
  display: inline-flex; align-items: center; justify-content: center; box-sizing: border-box;
  min-width: 20px; height: 20px; padding: 0 5px; border-radius: 0; color: #fff;
  font-size: 11px; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; letter-spacing: .01em;
}
.pt-chip.is-aktif { background: var(--pt-aktif); }
.pt-chip.is-lapse { background: var(--pt-lapse); }
.pt-chip.is-sp { background: var(--pt-sp); letter-spacing: .04em; }
.pt-chip.is-total { background: var(--pt-sp); }
.pt-chip.is-due { box-shadow: 0 0 0 2px #fff, 0 0 0 3px var(--pt-aktif); margin: 0 3px; }
a.pt-badges-link:hover .pt-chip, a.pt-badges-link:focus-visible .pt-chip { filter: brightness(1.15); }
a.pt-badges-link:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.pt-zero { color: var(--muted); font-variant-numeric: tabular-nums; }

/* The six squares that fill as points near the threshold. */
.pt-meter { display: inline-flex; gap: 3px; margin-bottom: 4px; }
.pt-meter i { width: 14px; height: 14px; border: 1px solid #b9b9b9; background: #fff; box-sizing: border-box; }
.pt-meter i.on { background: var(--pt-aktif); border-color: var(--pt-aktif); }
.pt-meter-lg { gap: 6px; margin: 10px 0 10px; }
.pt-meter-lg i { width: 34px; height: 34px; }
.pt-due { display: block; color: var(--pt-aktif); font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; }
.pt-table tr.is-due td:first-child { box-shadow: inset 3px 0 0 var(--pt-aktif); }
.pt-table .pt-num, .pm-table .pt-num { text-align: center; width: 72px; }
.pt-table tr.is-lapse-row td { color: var(--muted); }
.pt-table tr.is-lapse-row .pt-weight { opacity: .55; }

.pt-weight {
  display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px;
  border: 1px solid var(--ink); font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums;
}
.pt-weight.w3 { background: #f1ece9; }
.pt-weight.w4 { background: #e8cfc8; border-color: var(--pt-aktif); }
.pt-weight.w5 { background: var(--pt-aktif); border-color: var(--pt-aktif); color: #fff; }

.pt-state { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; }
.pt-state.is-aktif { color: var(--pt-aktif); }
.pt-state.is-lapse { color: var(--pt-lapse); }
.pt-state.is-sp { color: var(--pt-sp); }

.pt-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 0;
  margin: 0 0 20px; border: 1px solid var(--line); }
.pt-stats > div { padding: 14px 18px; border-left: 1px solid var(--line); }
.pt-stats > div:first-child { border-left: 0; }
.pt-stats dt { color: var(--muted); font-size: 12px; text-transform: uppercase; letter-spacing: .04em; }
.pt-stats dd { margin: 6px 0 0; font-size: 28px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.1; }
.pt-stats .is-due dd { color: var(--pt-aktif); }
.pt-inline { display: inline-flex; gap: 6px; align-items: center; margin: 0; }
.pt-stats .pt-inline input { width: 110px; min-height: 36px; font-size: 16px; font-weight: 700; padding: 0 10px;
  border: 1px solid var(--line); background: var(--field); font-variant-numeric: tabular-nums; }
.pt-stats .pt-inline .btn { min-height: 36px; padding: 0 14px; font-size: 14px; width: auto; }

.pt-hero { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0; margin: 0 0 28px;
  border: 1px solid var(--line); }
.pt-card { padding: 18px 20px; border-left: 1px solid var(--line); display: flex; flex-direction: column; gap: 6px; }
.pt-card:first-child { border-left: 0; }
.pt-card-wide { grid-column: 1 / -1; border-left: 0; border-top: 1px solid var(--line); }
.pt-card.is-due { background: #fbf3f1; }
.pt-label { color: var(--muted); font-size: 12px; text-transform: uppercase; letter-spacing: .04em; font-weight: 600; }
.pt-big { font-size: 44px; line-height: 1; font-weight: 700; font-variant-numeric: tabular-nums; }
.pt-big.is-aktif { color: var(--pt-aktif); }
.pt-big.is-lapse { color: var(--pt-lapse); }
.pt-big.is-sp { color: var(--pt-sp); }
.pt-note { color: var(--muted); font-size: 13px; }
.pt-issue { display: flex; flex-direction: column; gap: 10px; align-items: flex-start; }
.pt-issue .btn { width: auto; }
.pt-levels { display: flex; gap: 8px; flex-wrap: wrap; }
.pt-levels label { display: inline-flex; align-items: center; gap: 8px; padding: 8px 14px; border: 1px solid var(--line);
  background: #fff; cursor: pointer; font-weight: 700; }
.pt-levels label:has(input:checked) { border-color: var(--ink); background: var(--ink); color: #fff; }
.pt-levels input { accent-color: var(--ink); }
.pt-levels em { font-style: normal; font-weight: 500; font-size: 12px; opacity: .75; }
.pt-h { font-size: 13px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); margin: 8px 0 10px; }

.pm-table td { vertical-align: top; }
.pm-text { max-width: 560px; line-height: 1.45; }
.pm-kel { color: var(--muted); font-size: 13px; max-width: 280px; }
.pm-src { display: inline-block; padding: 1px 6px; border: 1px solid var(--line); font-size: 11px; font-weight: 700;
  letter-spacing: .04em; white-space: nowrap; }
.pm-act { white-space: nowrap; text-align: right; vertical-align: top; }
.pm-table td.pm-act { padding-top: 6px; }
.pm-act .aksi-link { background: none; border: 0; cursor: pointer; font: inherit; padding: 4px 6px; }
.pm-hapus { color: var(--pt-aktif); }

.pt-dialog { width: min(560px, calc(100vw - 32px)); border: 1px solid var(--ink); padding: 24px; }
.pt-dialog::backdrop { background: rgba(20, 20, 20, 0.45); }
.pt-dialog h2 { margin: 0 0 14px; font-size: 18px; }
.pt-dialog-actions { margin-top: 18px; }
.pt-file { display: flex; flex-direction: column; gap: 6px; font-size: 13px; }

/* "Beri poin" dialog on Data Complain / Findings SQ. */
.pt-give-hint { margin: 0 0 12px; }
.pt-pick-info { margin: 6px 0 0; padding: 10px 12px; border: 1px solid var(--line); background: #f8f8f6; font-size: 13px; }
.pt-pick-info .pt-weight { margin-right: 8px; }

.pt-cell { display: inline-flex; align-items: center; gap: 2px; flex-wrap: wrap; }

@media (max-width: 720px) {
  .pt-hero { grid-template-columns: 1fr; }
  .pt-card { border-left: 0; border-top: 1px solid var(--line); }
  .pt-card:first-child { border-top: 0; }
  .pt-meter-lg i { width: 28px; height: 28px; }
}
/* A badge inside a stretched-link row sits above the stretch so it stays clickable on its own. */
.baris-link ~ .pt-badges, .scope-line .pt-badges { position: relative; z-index: 2; }
.sq-who-tag { display: block; font-size: 12px; font-weight: 600; color: var(--ink); }
.sq-who { grid-column: 1 / -1; }
.pt-table .baris-link { color: var(--ink); font-weight: 600; text-decoration: none; }
.pt-table .baris-link:hover, .pt-table .baris-link:focus-visible { color: var(--brand); text-decoration: underline; text-decoration-thickness: 1px; }
/* Level picker: the chip is the control; the radio stays for keyboards and screen readers. */
.pt-levels { gap: 0; }
.pt-levels label { position: relative; padding: 10px 18px; margin-left: -1px; }
.pt-levels label:first-child { margin-left: 0; }
.pt-levels input { position: absolute; opacity: 0; inset: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.pt-levels label:has(input:focus-visible) { outline: 2px solid var(--brand); outline-offset: 2px; z-index: 1; }
.pt-levels label:has(input:checked) { z-index: 1; }
/* The complaint can run to a page; the dialog is about the points, so show a few lines and scroll the rest. */
.pt-give .pt-give-hint { max-height: 4.6em; overflow: auto; padding: 8px 10px; background: #f8f8f6; border-left: 3px solid var(--line);
  font-size: 13px; line-height: 1.5; }
.pt-give .pt-pick { font-size: 13px; }
.pt-give .pt-pick option { padding: 5px 8px; }
/* Nomor SP: input and Simpan share one height and baseline. */
.pt-stats .pt-inline { align-items: stretch; gap: 8px; margin-top: 2px; }
.pt-stats .pt-inline input, .pt-stats .pt-inline .btn {
  box-sizing: border-box; height: 38px; min-height: 38px; margin: 0; padding: 0 14px; line-height: 1; align-self: stretch;
  display: inline-flex; align-items: center; }
.pt-stats .pt-inline input { width: 110px; }
.arsip-selisih { margin: 0 0 16px; padding: 10px 14px; border: 1px solid var(--line); border-left: 3px solid var(--pt-aktif, #a3301a);
  background: #fbf3f1; font-size: 13px; line-height: 1.45; }
.arsip-selisih strong { display: block; color: var(--pt-aktif, #a3301a); }
.arsip-selisih ul { list-style: none; margin: 6px 0 0; padding: 0; }
.arsip-selisih li { margin: 3px 0; }
.arsip-selisih a { font-weight: 600; color: var(--ink); }
.arsip-selisih li span { color: var(--muted); }

/* --- Grovv Instagram page (social module) ------------------------------- */
/* The page is a magazine spread, not a form: one score band, one drawn
   series, one breakdown, one list. Every rule here is scoped to `.ig` so
   nothing leaks into the BSC dashboards. */
.page.ig { width: 100%; }
.ig { --ig-hair: var(--line); }
.ig .dash-head { align-items: center; }
.ig-who { flex-direction: column; align-items: flex-start; gap: 2px; }
.ig-handle { font-size: 13px; font-weight: 700; letter-spacing: .02em; color: var(--ink); }
.ig-who .scope-line { margin: 0; font-size: 12px; }
.ig-controls { display: flex; align-items: center; gap: 14px; }
.ig-sync-state { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; color: var(--muted); }
.ig-pulse { width: 6px; height: 6px; border-radius: 50%; background: var(--brand); animation: ig-pulse 1.1s ease-in-out infinite; }
@keyframes ig-pulse { 50% { opacity: .25; } }
.ig .ig-sync-btn {
  height: 32px; min-height: 0; padding: 0 12px; font-size: 12px; font-weight: 600;
  background: transparent; color: var(--ink); border: 1px solid var(--line); border-radius: 0;
}
.ig .ig-sync-btn:hover { border-color: var(--ink); background: transparent; }

/* Score band: four figures separated by hairlines, the follower given the top
   rule — the one number this page is about. */
.ig-score-card { margin-bottom: 16px; }
.ig-score { display: grid; grid-template-columns: 1.25fr 1fr 1fr 1fr; border-top: 1px solid var(--ig-hair); }
.ig-cell { min-width: 0; padding: 16px 20px 4px; border-left: 1px solid var(--ig-hair); }
.ig-cell:first-child { border-left: 0; padding-left: 0; }
.ig-cell:last-child { padding-right: 0; }
.ig-label { display: block; font-size: 11px; font-weight: 600; letter-spacing: .09em; text-transform: uppercase; color: var(--muted); }
.ig-value { display: block; margin-top: 8px; font-size: clamp(22px, 2.2vw, 32px); font-weight: 700; letter-spacing: -.02em; line-height: 1; font-variant-numeric: tabular-nums; }
.ig-big { display: block; margin-top: 6px; font-size: clamp(32px, 3.6vw, 52px); font-weight: 700; letter-spacing: -.03em; line-height: .95; font-variant-numeric: tabular-nums; }
.ig-foot { display: block; margin-top: 8px; font-size: 12px; color: var(--muted); }
.ig-lead .ig-label { color: var(--brand); }

/* Two-up body: the series takes twice the width of its breakdown. */
.ig-grid { display: grid; grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr); gap: 16px; margin-bottom: 16px; align-items: stretch; }
.ig-grid > .dash-card { display: flex; flex-direction: column; }
.ig-note { margin: 10px 0 0; font-size: 12px; line-height: 1.5; color: var(--muted); }

/* Daily reach: stretched SVG (percent geometry) + HTML ticks and one direct
   label at the peak. Same idiom as the outlet card, no chart bundle. */
.ig-chart { display: grid; grid-template-columns: 44px minmax(0, 1fr); grid-template-rows: minmax(170px, 1fr) 26px; flex: 1; margin-top: 4px; font-variant-numeric: tabular-nums; }
.ig-y { position: relative; grid-column: 1; grid-row: 1; }
.ig-y span { position: absolute; right: 8px; transform: translateY(-50%); font-size: 11px; color: var(--muted); }
.ig-plot { position: relative; grid-column: 2; grid-row: 1; }
.ig-plot svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.ig-plot line { stroke: var(--line-soft); stroke-width: 1; vector-effect: non-scaling-stroke; }
.ig-area { fill: var(--brand); opacity: .09; }
.ig-line { fill: none; stroke: var(--brand); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.ig-peak {
  position: absolute; transform: translate(-50%, -140%); padding: 0 4px;
  font-size: 12px; font-weight: 700; color: var(--ink); white-space: nowrap;
  background: var(--bg); border-bottom: 2px solid var(--brand);
}
.ig-peak.is-right { transform: translate(-100%, -140%); }
.ig-peak.is-left { transform: translate(0, -140%); }
.ig-x { position: relative; grid-column: 2; grid-row: 2; }
.ig-x span { position: absolute; top: 6px; transform: translateX(-50%); font-size: 11px; color: var(--muted); white-space: nowrap; }
.ig-x span:first-child { transform: none; }
.ig-x span:last-child { transform: translateX(-100%); }

/* Interaction mix: one stacked bar, direct-labelled legend. Slots are the
   validated s1–s4 order, with the 2px surface gap the palette requires. */
.ig-eng-total { display: flex; align-items: baseline; gap: 8px; margin-bottom: 12px; }
.ig-eng-total strong { font-size: clamp(26px, 2.6vw, 38px); font-weight: 700; letter-spacing: -.02em; line-height: 1; font-variant-numeric: tabular-nums; }
.ig-eng-total span { font-size: 12px; color: var(--muted); }
.ig-split { display: flex; gap: 2px; height: 10px; background: var(--line-soft); }
.ig-split i { display: block; height: 100%; }
.is-like { background: var(--s1); }
.is-comment { background: var(--s2); }
.is-save { background: var(--s3); }
.is-share { background: var(--s4); }
.ig-legend { list-style: none; margin: 12px 0 0; padding: 0; }
.ig-legend li { display: grid; grid-template-columns: 10px 1fr auto 42px; align-items: center; gap: 9px; padding: 7px 0; border-bottom: 1px solid var(--line-soft); font-size: 13px; }
.ig-legend li i { width: 10px; height: 10px; }
.ig-legend b { font-weight: 700; font-variant-numeric: tabular-nums; }
.ig-legend span { text-align: right; font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.ig-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--ig-hair); }
.ig-pair strong { display: block; margin-top: 6px; font-size: 20px; font-weight: 700; font-variant-numeric: tabular-nums; }

/* Posts: an Instagram-style grid — square thumbnail, numbers beside it. Three
   across on a desktop, one on a phone; the thumbnail is what tells the reader
   which post a number belongs to. */
.ig-cards {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px;
}
.ig-card {
  display: grid; grid-template-columns: 128px minmax(0, 1fr); gap: 13px; min-width: 0;
  padding: 10px; border: 1px solid var(--line); background: var(--field);
}
.ig-card:hover, .ig-card:focus-within { border-color: var(--ink); }
.ig-thumb {
  position: relative; display: block; aspect-ratio: 1; overflow: hidden;
  background: #f1efec; color: var(--muted); text-decoration: none;
}
.ig-thumb img { position: relative; display: block; width: 100%; height: 100%; object-fit: cover; }
.ig-thumb-fallback {
  position: absolute; inset: 0; display: grid; place-items: center;
  font-size: 10px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
}
.ig-card-body { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.ig-card-meta { display: flex; align-items: center; gap: 8px; margin: 0; font-size: 12px; color: var(--muted); }
.ig-type {
  padding: 2px 6px; border: 1px solid var(--line);
  font-size: 9px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--muted);
}
.ig-type.is-video { border-color: var(--brand); color: var(--brand); }
.ig-type.is-carousel { border-color: var(--ink); color: var(--ink); }
.ig-card-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 12px; margin-block: auto; padding-top: 2px; }
.ig-card-stats span { display: block; font-size: 9px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.ig-card-stats b { display: block; font-size: 16px; font-weight: 700; line-height: 1.15; font-variant-numeric: tabular-nums; }
/* ER is the one figure that says whether a post worked, so it sits on the
   thumbnail itself — the eye lands there before it reads any number. */
.ig-er-badge {
  position: absolute; right: 6px; bottom: 6px; display: flex; align-items: baseline; gap: 4px;
  padding: 3px 6px; background: var(--ink); color: #fff;
  font-size: 9px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
}
.ig-er-badge b { color: #fff; font-size: 13px; font-weight: 700; letter-spacing: 0; text-transform: none; font-variant-numeric: tabular-nums; }
.ig-source { margin: 14px 0 0; }
.ig .dash-card h2 .ig-aside { margin-left: auto; font-size: 12px; font-weight: 600; letter-spacing: .02em; text-transform: none; color: var(--muted); font-variant-numeric: tabular-nums; }
.ig-engagement .ig-pair { margin-top: auto; }

@media (max-width: 1200px) { .ig-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

@media (max-width: 900px) {
  .ig-grid { grid-template-columns: minmax(0, 1fr); }
  .ig-score { grid-template-columns: 1fr 1fr; }
  .ig-cell { border-left: 0; border-top: 1px solid var(--ig-hair); padding: 14px 0 2px; }
  .ig-cell:first-child { border-top: 0; }
  .ig-cell:nth-child(even) { padding-left: 18px; border-left: 1px solid var(--ig-hair); }
}
@media (max-width: 720px) {
  .ig-head { grid-template-columns: minmax(0, 1fr); gap: 8px; }
  .ig-head .dash-head-titles,
  .ig-head .deltas,
  .ig-head .controls { grid-area: auto; text-align: left; justify-self: start; }
  .ig-head .dash-head-titles { order: -1; }
  .ig-controls { margin-top: 2px; }
  .ig-cards { grid-template-columns: minmax(0, 1fr); }
  .ig-card { grid-template-columns: 104px minmax(0, 1fr); }
}
.pt-give .bc-pop { max-height: 220px; }
.pt-give .bc-pop button { white-space: normal; }
.pt-give .bc-pop .pt-weight { margin-right: 10px; flex: none; }
.pt-give .bc-pop button:has(.pt-weight) { display: flex; align-items: flex-start; }
.pt-dialog.pt-give { overflow: visible; }
/* Letters still waiting for a signed PDF: on top of Poin & SP, and a count in the sidebar. */
.nav-count { display: inline-block; min-width: 18px; height: 18px; margin-left: 8px; padding: 0 5px; box-sizing: border-box;
  background: #a3301a; color: #fff; font-size: 11px; font-weight: 700; line-height: 18px; text-align: center; }
.pt-todo { margin: 0 0 28px; padding: 16px 18px 6px; border: 1px solid var(--ink); background: #fbf3f1; }
.pt-todo .pt-h { margin: 0 0 4px; }
.pt-todo .hint { margin: 0 0 10px; }
.pt-todo-n { display: inline-block; min-width: 20px; height: 20px; margin-left: 6px; padding: 0 6px; box-sizing: border-box;
  background: #a3301a; color: #fff; font-size: 12px; line-height: 20px; text-align: center; }
.pt-todo ul { list-style: none; margin: 0; padding: 0; }
.pt-todo li { display: flex; align-items: center; gap: 14px; padding: 10px 0; border-top: 1px solid #e8d6d1; }
.pt-todo-who { display: grid; flex: 1; min-width: 0; }
.pt-todo-who small { color: var(--muted); font-size: 12px; }
.pt-todo-age { color: var(--muted); font-size: 13px; white-space: nowrap; }
.pt-todo li.is-late .pt-todo-age { color: #a3301a; font-weight: 700; }
.pt-todo .btn { width: auto; white-space: nowrap; }
/* Employee Archive detail: Data karyawan | Poin & SP. */
.pt-tabs { display: flex; margin: 22px 0 24px; border-bottom: 1px solid var(--ink); }
.pt-tabs a { display: inline-flex; align-items: center; gap: 10px; height: 44px; padding: 0 22px; margin-bottom: -1px; color: var(--muted);
  font-size: 14px; font-weight: 700; text-decoration: none; border: 1px solid transparent; border-bottom: 0; }
.pt-tabs a:hover { color: var(--ink); }
.pt-tabs a.on { color: var(--ink); background: #fff; border-color: var(--ink); }
.pt-tabs a:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.pt-kosong { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 16px 20px; border: 1px dashed #b9b3ad;
  color: var(--muted); font-size: 14px; }
.pt-kosong .btn { width: auto; flex: none; }
.pt-bar { display: flex; justify-content: flex-end; margin: 0 0 14px; }
.pt-bar .btn { width: auto; }

/* Ask widget — a meeting follow-up modal. Ctrl/Cmd+S opens it; the trigger
   button is the discoverable path. No shadow: the panel is a hairline box. */
.ask-widget {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 60;
  font-family: var(--font);
}
.ask-trigger {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  width: auto;
  padding: 10px 14px;
  border: 1px solid var(--ink);
  background: var(--field);
  color: var(--ink);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.ask-trigger:hover { background: var(--ink); color: var(--field); }
.ask-trigger kbd {
  padding: 1px 5px;
  border: 1px solid currentColor;
  font: inherit;
  font-size: 10px;
  font-weight: 500;
  opacity: 0.75;
}
.ask-modal[hidden] { display: none; }
.ask-modal {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  padding: 24px;
}
.ask-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(26, 26, 26, 0.45);
}
.ask-panel {
  position: relative;
  width: min(760px, 100%);
  max-height: min(82vh, 760px);
  overflow: auto;
  padding: 22px;
  background: var(--bg);
  border: 1px solid var(--ink);
}
.ask-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
}
.ask-head h2 {
  margin: 0;
  color: var(--ink);
  font-size: 20px;
  font-weight: 700;
}
.ask-close {
  width: auto;
  padding: 2px 8px;
  border: 0;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
}
.ask-close:hover { color: var(--ink); }
.ask-form {
  display: flex;
  align-items: center;
  gap: 8px;
}
.ask-form input[type="search"] {
  flex: 1;
  width: 100%;
  min-width: 0;
  margin: 0;
  padding: 12px;
  border: 1px solid var(--ink);
  background: var(--field);
  color: var(--ink);
  font: inherit;
  font-size: 15px;
}
.ask-form input[type="search"]:focus {
  outline: 2px solid var(--brand);
  outline-offset: 1px;
}
.ask-form button {
  flex: 0 0 auto;
  width: auto;
  min-height: var(--tap);
  padding: 0 18px;
  border: 0;
  background: var(--ink);
  color: var(--field);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}
.ask-form button:hover { background: var(--brand); }
.ask-hint {
  margin: 10px 0 0;
  color: var(--muted);
  font-size: 12px;
  line-height: 1.5;
}
.ask-result:empty { display: none; }
.ask-answer {
  margin-top: 14px;
  padding: 16px 18px;
  background: var(--field);
  border: 1px solid var(--line);
  border-left: 4px solid var(--brand);
}
.ask-value {
  margin: 0;
  color: var(--ink);
  font-size: 32px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.ask-meta {
  margin: 4px 0 0;
  color: var(--muted);
  font-size: 12px;
  line-height: 1.5;
}
.ask-link {
  display: inline-block;
  margin-top: 10px;
  color: var(--brand);
  font-size: 13px;
  font-weight: 600;
}
.ask-muted { border-left-color: var(--line); }
.ask-muted ul {
  margin: 8px 0 0;
  padding-left: 18px;
  color: var(--muted);
  font-size: 12px;
  line-height: 1.6;
}
@media (max-width: 700px) {
  .ask-widget { right: 12px; bottom: 12px; }
  .ask-modal { padding: 12px; }
  .ask-panel { padding: 16px; }
  .ask-form { flex-direction: column; }
  .ask-form button { width: 100%; }
}

/* Content pillars: tabs on the Instagram page, a small editor for marketing. */
.ig-tabs { display: flex; flex-wrap: wrap; align-items: center; margin: 0 0 16px; border-bottom: 1px solid var(--line); }
.ig-tabs a {
  padding: 9px 14px; margin-bottom: -1px; border-bottom: 2px solid transparent;
  font-size: 12px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); text-decoration: none;
}
.ig-tabs a:first-child { padding-left: 0; }
.ig-tabs a:hover { color: var(--ink); }
.ig-tabs a.active { color: var(--ink); border-bottom-color: var(--brand); }
.ig-tabs .ig-tabs-manage {
  margin-left: auto; padding-right: 0; text-transform: none; letter-spacing: 0;
  font-size: 13px; color: var(--brand);
}
.ig-tabs .ig-tabs-manage:hover { text-decoration: underline; text-underline-offset: 2px; }
.ig-back { font-size: 13px; color: var(--muted); text-decoration: none; }
.ig-back:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 2px; }

.ig-pillar-card { margin-bottom: 16px; }
.ig-pillar-form { display: grid; grid-template-columns: minmax(180px, .8fr) minmax(240px, 1.4fr) auto; gap: 14px; align-items: end; }
.ig-pillar-form label { display: grid; gap: 6px; margin: 0; font-size: 11px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.ig-pillar-form input {
  height: 40px; min-height: 0; margin: 0; padding: 0 12px;
  border: 1px solid var(--line); border-radius: 0;
  background: var(--field); font: inherit; font-size: 14px; color: var(--ink);
}
.ig-pillar-form input:focus { outline: 2px solid var(--brand); outline-offset: -1px; }
.ig-pillar-actions { display: flex; align-items: center; gap: 12px; }
.ig-pillar-actions button { height: 40px; min-height: 0; margin: 0; padding: 0 18px; border: 0; background: var(--ink); color: #fff; font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; }
.ig-pillar-actions button:hover { background: var(--brand); }
.ig-pillar-actions a { font-size: 13px; color: var(--muted); }
.ig-pillar-table td, .ig-pillar-table th { vertical-align: middle; }
.ig-tag { display: inline-block; margin: 0 4px 4px 0; padding: 2px 7px; border: 1px solid var(--line); font-size: 12px; color: var(--muted); white-space: nowrap; }
.ig-pillar-row-actions { white-space: nowrap; width: 1%; }
.ig-pillar-row-actions form { display: inline; margin-left: 12px; }
.ig-pillar-row-actions button { padding: 0; border: 0; background: none; font: inherit; font-size: 13px; color: #a3301a; cursor: pointer; }
.ig-pillar-row-actions button:hover { text-decoration: underline; }
.ig-pillar-row-actions a { font-size: 13px; color: var(--ink); }
@media (max-width: 760px) {
  .ig-pillar-form { grid-template-columns: minmax(0, 1fr); }
  .ig-pillar-actions button { width: 100%; }
}

/* Instagram range toolbar, clickable score tiles, metric toggle, pager. */
.ig-range { display: flex; flex-wrap: wrap; gap: 12px 20px; align-items: center; justify-content: space-between; margin: 0 0 16px; }
.ig-presets { display: flex; flex-wrap: wrap; border: 1px solid var(--line); }
.ig-presets a {
  padding: 7px 12px; border-left: 1px solid var(--line);
  font-size: 11px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
  color: var(--muted); text-decoration: none;
}
.ig-presets a:first-child { border-left: 0; }
.ig-presets a:hover { color: var(--ink); background: #faf9f7; }
.ig-presets a.active { background: var(--ink); color: #fff; }
.ig-range-form { display: flex; align-items: end; gap: 10px; }
.ig-range-form label { display: grid; gap: 4px; margin: 0; font-size: 10px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.ig-range-form input[type=date] {
  height: 34px; min-height: 0; margin: 0; padding: 0 8px; border-radius: 0;
  border: 1px solid var(--line); background: var(--field); font: inherit; font-size: 13px; color: var(--ink);
}
.ig-range-form input[type=date]:focus { outline: 2px solid var(--brand); outline-offset: -1px; }
.ig-range-form button {
  height: 34px; min-height: 0; margin: 0; padding: 0 14px; border: 0;
  background: var(--ink); color: #fff; font: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
}
.ig-range-form button:hover { background: var(--brand); }

.ig-score a.ig-cell { display: block; color: inherit; text-decoration: none; }
.ig-score a.ig-cell:hover .ig-value, .ig-score a.ig-cell:hover .ig-big { color: var(--brand); }
.ig-score a.ig-cell.active { box-shadow: inset 0 3px 0 var(--brand); }

.ig-metric-toggle { display: inline-flex; border: 1px solid var(--line); margin: 0 0 10px; }
.ig-metric-toggle a {
  padding: 5px 10px; border-left: 1px solid var(--line);
  font-size: 11px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
  color: var(--muted); text-decoration: none;
}
.ig-metric-toggle a:first-child { border-left: 0; }
.ig-metric-toggle a.active { background: var(--ink); color: #fff; }

.ig-post-count { margin: 0 0 10px; text-align: right; }
.ig-pager {
  display: flex; align-items: center; justify-content: center; gap: 18px;
  margin: 16px 0 0; padding-top: 14px; border-top: 1px solid var(--line-soft);
  font-size: 13px; color: var(--muted);
}
.ig-pager a { color: var(--ink); font-weight: 600; text-decoration: none; }
.ig-pager a:hover { color: var(--brand); text-decoration: underline; text-underline-offset: 2px; }

@media (max-width: 760px) {
  .ig-range { flex-direction: column; align-items: flex-start; }
  .ig-range-form { flex-wrap: wrap; }
}
