/* ============================================================================
 * components.css — shared ui_upmedik component styles, loaded app-wide AFTER
 * the compiled Tailwind bundle (style.css) in every ui_upmedik layout. Plain
 * CSS on theme tokens; no Tailwind rebuild needed. See -UPMEDIK_PREREQ.md.
 * ==========================================================================*/

/* Header action pills — Edit / PDF / Back all share ONE size & shape (pill).
 * Colour is the only thing that varies (variants below). See -UPMEDIK_PREREQ.md §5. */
.up-pill {
    display: inline-flex; align-items: center; justify-content: center; gap: .35rem;
    height: 2rem; padding: 0 1rem;
    border-radius: 9999px;                       /* pill (rounded-full — exempt from the .4rem rule) */
    font-size: .8rem; font-weight: 600; line-height: 1; white-space: nowrap;
    border: 1px solid transparent;
    text-decoration: none; cursor: pointer;
    transition: background-color .12s ease, color .12s ease, border-color .12s ease, opacity .12s ease;
}
.up-pill svg { height: .9rem; width: .9rem; flex-shrink: 0; }
/* .up-pill sets display:inline-flex; same-specificity + later-in-cascade beats
 * Tailwind's .hidden{display:none} from style.css, so a pill toggled hidden via
 * JS (classList.toggle('hidden', ...)) stayed visible. Two-class selector wins
 * on specificity regardless of source order — no !important needed. */
.up-pill.hidden { display: none; }

/* Solid primary (e.g. Edit). */
.up-pill--primary { background: var(--primary); color: var(--primary-foreground); }
.up-pill--primary:hover { opacity: .9; }

/* Locked — the action exists but this user may not perform it. Shown INSTEAD of
 * hiding the control, so the reason is on screen ([title]) rather than left to
 * the user to guess from a button that is not there. Not a button: render it as
 * a <span> so there is nothing to click and nothing to focus. */
.up-pill--locked {
    background: var(--secondary); color: var(--muted-foreground);
    border-color: var(--border); cursor: not-allowed; opacity: .7;
}

/* PDF export — green (e.g. Cetak Biodata → PDF). */
.up-pill--pdf { background: var(--emerald, #10b981); color: #fff; }
.up-pill--pdf:hover { opacity: .9; }

/* Unified "back to previous page" button (-UPMEDIK_PREREQ.md §5). Size/shape come
 * from `.up-pill` (the partial adds both classes). LIGHT-primary by default →
 * solid primary on hover. `margin-left:auto` pushes it to the RIGHT of its flex
 * header row. Render via `_partials/back_button`; never hand-roll one. */
.up-back {
    margin-left: auto;
    background: color-mix(in oklch, var(--primary) 12%, transparent);
    color: var(--primary);
    border-color: color-mix(in oklch, var(--primary) 22%, transparent);
}
.up-back:hover { background: var(--primary); color: var(--primary-foreground); border-color: var(--primary); }

/* ---------------------------------------------------------------------------
 * Selection pills / button-select — ONE definition for /elements and for every
 * panel that uses them. Three states, on request:
 *
 *     inactive  grey   (secondary)
 *     hover     blue   (primary)
 *     selected  blue   (primary)
 *
 * Written HERE, not as Tailwind utilities, for two reasons: the bundle is
 * prebuilt and purged so a hover:border-* that no source file already uses does
 * not exist at runtime, and an INLINE style (which the datatable02 filter pills
 * use for their state) can never be overridden by a :hover class. A real class
 * with a real :hover rule is the only spelling that does all three.
 * ------------------------------------------------------------------------- */
.up-select-pill {
    background: var(--secondary);
    color: var(--secondary-foreground);
    border-color: var(--secondary);
    transition: background-color .12s ease, color .12s ease, border-color .12s ease;
}
.up-select-pill:hover {
    background: var(--primary);
    color: var(--primary-foreground);
    border-color: var(--primary);
}
/* Selected wins over hover — the same blue, so moving the mouse across the
   chosen pill never makes it look like it changed. */
.up-select-pill[data-active],
.up-select-pill[aria-pressed="true"] {
    background: var(--primary);
    color: var(--primary-foreground);
    border-color: var(--primary);
}

/* ── Table header band — ONE definition, app-wide ─────────────────────────────
   Every <th> in the app carries the same 20%-primary tint (-UPMEDIK_PREREQ.md
   §10). It lives here rather than in each table's class string because that is
   exactly what went wrong: the tint was typed per table and drifted into four
   different answers — bg-primary/30 in the list views, bg-primary/20 in the
   charts shell, bg-primary/5 in one report, and nothing at all in the ERM
   panels and reg-detail tables. Four spellings, one intention.

   Keyed on the CELL, so a table built in JS (UpTable, the charts shell) gets the
   band without its renderer knowing about it — and so do the header rows written
   as a bare <tr> with no <thead> around them, which several report pages use.
   -UPMEDIK_PREREQ.md §10 words it the same way: every <th>, any table.

   Header text keeps the colour it already had — the tint is a band, not a theme. */
th,
.up-panel-head {
    background-color: color-mix(in oklch, var(--primary) 20%, transparent);
}

/* A section header inside a panel — "I. Pengkajian", "II. Pendidikan Kesehatan".
   It shares the declaration above deliberately: the header band was written as
   `bg-primary/15`, a DIFFERENT opacity of the same hue, and at 15% over the dark
   card it composites toward violet while the table headers beside it stay blue.
   Two blues in one panel read as two colours, so there is one. */
.up-panel-head { }

/* ── Fold card — a card whose body collapses ──────────────────────────────────
   The ui_upmedik answer to Bootstrap's `data-bs-toggle="collapse"`. Built on
   <details>/<summary>, so open/close costs no JS and survives with the Bootstrap
   bundle gone; the shell already keeps that state per element.

   Usage:
     <details class="up-fold rounded-xl border border-border bg-card shadow-sm
                     overflow-hidden" open>
       <summary class="up-panel-head flex items-center gap-3 px-4 py-3 …">
         <h3>…</h3><svg class="up-fold-chev ml-auto …">…</svg>
       </summary>
       <div class="p-4">…</div>
     </details>

   `list-style:none` + the webkit rule kill the native disclosure triangle
   (Safari needs the ::-webkit- one even when the summary is display:flex).

   NOT for dropdown MENUS. Those have their own contract already — the
   `data-upx-dropdown` / `-dd-trigger` / `-dd-menu` trio from the golden element
   at /elements#menus. One shape per job; a <details>-based menu next to it would
   be a second dropdown idiom in the same app. */
.up-fold > summary { list-style: none; cursor: pointer; user-select: none; }
.up-fold > summary::-webkit-details-marker { display: none; }
.up-fold-chev { transition: transform .15s ease; }
.up-fold[open] > summary .up-fold-chev { transform: rotate(180deg); }

/* ── Solid row-action buttons — ONE definition, app-wide ──────────────────────
   -UPMEDIK_PREREQ.md §7b: every button inside a table row / item-repeater is a
   SOLID fill with contrasting text. The fill hues live HERE, not in per-page
   class strings, because the first sweep mapped them onto the theme tokens
   (--teal 70% L, --rose 68% L) — pastel palette values meant for badges — and
   every Edit/Hapus button came out washed out. Buttons use the same 600-level
   strength as the approved accents (violet #7c3aed, export green #16a34a).

   Usage: class="up-btn-teal" (Edit) · "up-btn-amber" (Batal/Tolak) ·
   "up-btn-rose" (Hapus/destructive). Sizing/typography stays with the caller. */
.up-btn-teal  { background-color: #0d9488; color: #fff; }
.up-btn-amber { background-color: #d97706; color: #fff; }
.up-btn-rose  { background-color: #e11d48; color: #fff; }
.up-btn-teal:hover,
.up-btn-amber:hover,
.up-btn-rose:hover { opacity: .9; }

/* Workflow accent button (Approve / Mutasi / Proses RO) — same violet as the
   approved #7c3aed accent, solid per §7b. */
.up-btn-violet { background-color: #7c3aed; color: #fff; }
.up-btn-violet:hover { opacity: .9; }

/* ── Status badges / pills — SOLID by default, ONE definition ─────────────────
   Standing user decision (2026-08-10): EVERYTHING is solid unless the user
   explicitly asks for soft. The old `bg-<hue>/15 text-<hue>` badge tints made
   status chips look washed out / disabled. Same 600-level hues as the buttons;
   no hover (badges are not controls). */
.up-badge-emerald { background-color: #16a34a; color: #fff; }
.up-badge-amber   { background-color: #d97706; color: #fff; }
.up-badge-rose    { background-color: #e11d48; color: #fff; }
.up-badge-teal    { background-color: #0d9488; color: #fff; }
.up-badge-violet  { background-color: #7c3aed; color: #fff; }
.up-badge-muted   { background-color: #64748b; color: #fff; }

/* ==========================================================================
 * THE WORKSPACE SHELL — REGBAR + ACTIONBAR
 *
 * ONE definition, read by BOTH shells: /charts (_layouts/charts/shell_master.php)
 * and every main_master page that calls _shared/_workspace_shell.php. These rules
 * lived inline in shell_master's <style>, which meant a second page could copy
 * the markup and silently get none of the behaviour — the dense inline row, the
 * 2-line clamp, the phone collapse. Moved here so the region is one thing.
 *
 * Selectors are CLASSES (.hms-bio), not ids: a page may hold one of these, and
 * an id cannot be reused. shell_master's #hms-bio div carries the class too.
 * ========================================================================== */

/* ACTION BAR — SOLID PRIMARY PILLS (user, 2026-08-14).
 *
 * These were plain text verbs separated by bullets, on the reasoning that a row
 * of eight solid pills reads as eight competing primary actions. Overruled: a
 * row of muted text does not read as a row of CONTROLS at all, and on this bar
 * every entry is something you can actually do to the registration. The app's
 * standing rule is solid by default anyway (-UPMEDIK_PREREQ §7b).
 *
 * ONE definition here rather than a class string per call site — the bar is
 * built in _shared/_workspace_bars.php for both shells, so the look changes in
 * one place for every page that has an action bar.
 *
 * DISABLED renders as a <span> (see _workspace_bars.php), so it keeps the pill
 * shape but is not clickable or focusable; the [title] carries the reason. */
.actionbar-btn {
    display: inline-flex; align-items: center; justify-content: center;
    height: 1.75rem; padding: 0 .75rem;
    border-radius: 9999px;                       /* pill */
    border: 1px solid transparent;
    background: var(--primary); color: var(--primary-foreground);
    font-size: .75rem; font-weight: 600; line-height: 1; white-space: nowrap;
    text-decoration: none; cursor: pointer;
    transition: opacity .12s ease;
}
.actionbar-btn:hover { opacity: .9; }
.actionbar-btn:disabled,
span.actionbar-btn { opacity: .5; cursor: not-allowed; }

/* TONE — the two actions that must not read as "one of the others".
 *
 * The bar is a row of identical primary pills, which is right for a row of
 * ordinary actions and wrong for these two: BATAL destroys a registration, and
 * CETAK opens a document. Both are found by colour before they are read, and on
 * a bar this long that is the difference between reaching for the right pill and
 * reading eight labels.
 *
 * Solid, never a tint — the same palette the tables use (-UPMEDIK_PREREQ §7b):
 * destructive rose for the cancels, #16a34a green for anything that prints. The
 * disabled rule above still wins, so a greyed Cetak stays grey. */
.actionbar-btn--danger { background: var(--destructive, #e11d48); color: #fff; }
.actionbar-btn--print  { background: var(--green, #16a34a);       color: #fff; }

/* REGBAR biodata — the dense `Label : Value ·` run.
 * Base (mobile-first) is the label-over-value grid; the media queries below turn
 * it into the inline row. Driven here rather than with sm:/lg: utilities because
 * the base `grid`/`uppercase` classes would win the cascade. */
.hms-bio-toggle { display: none; }

/* PHONE (< 640): show ONLY the fields flagged `id` (ID Reg / ID Visit), laid out
 * INLINE ("ID Reg : 13486") rather than stacked, so the sticky bar cannot eat a
 * phone screen. The rest is reachable through the Reg Details button. */
@media (max-width: 639.98px) {
    .hms-bio .hms-bio-item:not(.hms-bio-id) { display: none; }
    .hms-bio > .hms-bio-sep { display: none; }          /* drop the bullet separators */
    .hms-bio { display: flex; flex-wrap: wrap; column-gap: .9rem; row-gap: .1rem; }
    .hms-bio .hms-bio-id { flex-direction: row; align-items: baseline; gap: .3rem; white-space: nowrap; }
    .hms-bio .hms-bio-id .hms-bio-label { text-transform: none; letter-spacing: normal; font-size: .72rem; }
    .hms-bio .hms-bio-id .hms-bio-sep { display: inline; }   /* the ":" after the label */
}

/* TABLET + DESKTOP (>= 640): the dense inline bullet row — fills the width,
 * never the sparse label-over-value grid. Clamped to ~2 lines; a page may reveal
 * a "Reg Details" control when it overflows. */
@media (min-width: 640px) {
    .hms-bio { display: flex; flex-wrap: wrap; align-items: center; column-gap: .5rem; row-gap: .25rem; }
    .hms-bio .hms-bio-item  { flex-direction: row; align-items: center; gap: .25rem; white-space: nowrap; flex: 0 0 auto; }
    .hms-bio .hms-bio-val   { overflow: visible; text-overflow: clip; }
    .hms-bio .hms-bio-sep   { display: inline; }
    .hms-bio .hms-bio-label { text-transform: none; letter-spacing: normal; font-size: .75rem; }
    .hms-bio.hms-bio-clamp  { max-height: 2.6rem; overflow: hidden; }  /* ~2 lines */
}

/* SIDE-PANE CHEVRON — the one collapse/expand control, on both shells.
 *
 * NO resting fill. It sits ON the dark pane, and a filled rounded square there
 * reads as a second, heavier UI element competing with the pane's own content;
 * /charts had `bg-sidepane-hover` baked in, which is why the same control looked
 * like two different buttons depending on which page you were on. The fill is
 * the HOVER state and nothing else — the chevron itself is the affordance.
 *
 * Direction is the state: "<" collapses, ">" re-opens. main_master flips one
 * glyph with scaleX(-1); /charts ships two buttons, one per state.
 */
.sidepane-chevron {
    display: grid; place-items: center; flex-shrink: 0;
    height: 1.5rem; width: 1.5rem;
    border-radius: .375rem;
    background: none; border: 0; padding: 0; cursor: pointer;
    color: var(--sidepane-muted);
    transition: background-color .12s ease, color .12s ease;
}
.sidepane-chevron:hover { background-color: var(--sidepane-hover); color: var(--sidepane-fg); }
.sidepane-chevron svg { height: 1rem; width: 1rem; }

/* ── A CARD INSIDE THE SIDE PANE, LIT BY WHICH ONE YOU ARE SHOWING ───────────
 * The pane itself stays dark. What decides a card's SURFACE is whether it is the
 * registration currently open in the content area — not whether it is still an
 * open visit:
 *
 *   SHOWN     — a LIGHT card. It is the one the whole right-hand side is about,
 *               so it steps forward off the dark pane.
 *   THE REST  — DARK cards. They recede into the pane's own tone and are read
 *               only when you go looking for them.
 *
 * AKTIF vs ARSIP is a different question and the badges already answer it: green
 * for a live registration, rose for a closed one, on either surface. Driving the
 * surface off the badge instead meant clicking SHOW on an archived visit lit up
 * everything EXCEPT the one you had just opened.
 *
 * Neither variant fights the pane's rules selector by selector. Each REDECLARES
 * the --sidepane-* tokens for its own subtree, so the layout's
 * `#app-sidebar .text-muted-foreground { color: var(--sidepane-muted) }` lands
 * on the page's muted grey inside a light card and on the pane's pale grey
 * inside a dark one. One rule, two surfaces, no specificity race — and it holds
 * under either shell, because it never names the pane's id.
 *
 * --border goes with them, so `border-border` (the card's own edge, and the
 * hairline down the visits list inside it) follows the surface it is drawn on
 * instead of cutting a light line across a dark card. `border-primary` is a
 * different token and survives, which is why the shown card keeps its blue edge.
 */
.ws-card {
    background-color: var(--ws-card-surface);
    color: var(--sidepane-fg);
}
/* THE ONE BEING SHOWN.
 *
 * A wash of primary over the card, not plain white: the light/dark split already
 * says which one this is, and the tint keeps it reading as the SELECTED card
 * rather than merely the pale one.
 *
 * sRGB, not oklch: --card is white, which is achromatic, and an oklch mix
 * interpolates its UNDEFINED hue toward 0 — the "6% blue" came out visibly pink.
 * sRGB has no hue to interpolate, so the tint is the primary. */
.ws-card[data-rail-current="1"] {
    --ws-card-surface: color-mix(in srgb, var(--primary) 6%, var(--card));
    --sidepane-fg:     var(--card-foreground);
    --sidepane-muted:  var(--muted-foreground);
    --sidepane-border: var(--border);
}
/* EVERY OTHER REGISTRATION. Lifted a few percent off the pane rather than set to
 * it exactly: at the pane's own colour the card is an outline with nothing in
 * it. */
.ws-card:not([data-rail-current="1"]) {
    --ws-card-surface: color-mix(in srgb, var(--sidepane-fg) 6%, var(--sidepane-bg));
    --border:          var(--sidepane-border);
}
