/* Natans Offset — Admin dashboard styles
   Scoped to .admin-body so nothing leaks into the public landing page. */

.admin-body {
    --adm-bg: #f1f5f9;
    --adm-surface: #ffffff;
    --adm-border: #e2e8f0;
    --adm-text: #0f172a;
    --adm-muted: #64748b;
    --adm-primary: #ea580c;
    --adm-sidebar: #0f172a;
    min-height: 100vh;
    margin: 0;
    background: var(--adm-bg);
    color: var(--adm-text);
    font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

/* ---------------------------------------------------------------- topbar */
.admin-topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: .75rem 1.25rem;
    background: var(--adm-surface);
    border-bottom: 1px solid var(--adm-border);
    position: sticky;
    top: 0;
    z-index: 20;
}

.admin-brand {
    display: flex;
    align-items: center;
    gap: .65rem;
    font-size: .95rem;
}

.admin-brand small {
    display: block;
    color: var(--adm-muted);
    font-size: .72rem;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.admin-logo {
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    border-radius: 9px;
    background: var(--adm-primary);
    color: #fff;
    font-weight: 700;
}

.admin-topbar-right {
    display: flex;
    align-items: center;
    gap: .75rem;
    flex-wrap: wrap;
}

/*
 * The logout control is a <form>, so it inherits the browser's default block
 * margins and would sit misaligned next to the other topbar links. Flatten it
 * so it behaves like the <a> it replaced.
 */
.admin-logout-form {
    display: inline-flex;
    margin: 0;
}

.admin-link-muted,
.admin-user-chip {
    color: var(--adm-muted);
    font-size: .85rem;
    text-decoration: none;
    white-space: nowrap;
}

/* ----------------------------------------------------------------- shell */
.admin-shell {
    display: flex;
    align-items: flex-start;
    min-height: calc(100vh - 56px);
}

.admin-sidebar {
    flex: 0 0 236px;
    background: var(--adm-sidebar);
    padding: 1.25rem .85rem;
    position: sticky;
    top: 56px;
    min-height: calc(100vh - 56px);
}

.admin-nav-link {
    display: flex;
    align-items: center;
    gap: .65rem;
    padding: .6rem .8rem;
    margin-bottom: .2rem;
    border-radius: 8px;
    color: #cbd5f5;
    font-size: .88rem;
    text-decoration: none;
    transition: background .15s ease;
}

.admin-nav-link:hover {
    background: rgba(255, 255, 255, .08);
    color: #fff;
}

.admin-nav-link.is-active {
    background: var(--adm-primary);
    color: #fff;
}

.admin-badge {
    margin-left: auto;
    padding: .05rem .4rem;
    border-radius: 999px;
    background: #ef4444;
    color: #fff;
    font-size: .7rem;
    font-style: normal;
    font-weight: 600;
}

.admin-main {
    flex: 1 1 auto;
    min-width: 0;
    padding: 1.5rem;
}

/* ---------------------------------------------------------------- alerts */
.admin-alert {
    display: flex;
    align-items: center;
    /* Needed by .admin-alert-details: without wrapping, a 100%-basis child would
       be squeezed into the same row as the message instead of starting a new one. */
    flex-wrap: wrap;
    gap: .5rem;
    margin-bottom: 1rem;
    padding: .7rem .9rem;
    border-radius: 8px;
    font-size: .88rem;
}

.admin-alert-success {
    background: #dcfce7;
    color: #14532d;
}

.admin-alert-error {
    background: #fee2e2;
    color: #7f1d1d;
}

/*
 * Per-file detail list inside a flash alert.
 *
 * The alert is a flex row, so a nested <ul> would be laid out as another flex
 * item and sit beside the message instead of under it. `flex-wrap` plus
 * `flex-basis: 100%` gives the list its own line without disturbing the icon and
 * text above.
 */
.admin-alert-details {
    flex: 0 0 100%;
    margin: .25rem 0 0;
    padding-left: 1.1rem;
    font-size: .82rem;
}

/* ----------------------------------------------------------------- cards */
.admin-page-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    margin-bottom: 1.25rem;
}

.admin-page-head h1 {
    margin: 0;
    font-size: 1.35rem;
    font-weight: 650;
}

.admin-page-head p {
    margin: .2rem 0 0;
    color: var(--adm-muted);
    font-size: .85rem;
}

.admin-card {
    background: var(--adm-surface);
    border: 1px solid var(--adm-border);
    border-radius: 12px;
    padding: 1.15rem 1.25rem;
    margin-bottom: 1.25rem;
}

/* Ties the analytics card accents to the site theme colour. */
.text-orange {
    color: #ea580c;
}

.admin-card > h2 {
    margin: 0 0 .9rem;
    font-size: 1rem;
    font-weight: 620;
}

.admin-stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
    gap: 1rem;
    margin-bottom: 1.25rem;
}

.admin-stat {
    background: var(--adm-surface);
    border: 1px solid var(--adm-border);
    border-radius: 12px;
    padding: 1rem 1.1rem;
}

.admin-stat .label {
    color: var(--adm-muted);
    font-size: .78rem;
    text-transform: uppercase;
    letter-spacing: .04em;
}

.admin-stat .value {
    font-size: 1.75rem;
    font-weight: 680;
    line-height: 1.15;
    margin-top: .2rem;
}

.admin-stat .value small {
    font-size: .85rem;
    font-weight: 500;
    color: var(--adm-muted);
}

/* ---------------------------------------------------------------- tables */
.admin-table {
    width: 100%;
    border-collapse: collapse;
    font-size: .87rem;
}

.admin-table th,
.admin-table td {
    padding: .6rem .55rem;
    border-bottom: 1px solid var(--adm-border);
    text-align: left;
    vertical-align: middle;
}

.admin-table th {
    color: var(--adm-muted);
    font-size: .76rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
}

.admin-table tbody tr:last-child td {
    border-bottom: 0;
}

.admin-table .col-actions {
    text-align: right;
    white-space: nowrap;
}

.admin-pill {
    display: inline-block;
    padding: .1rem .5rem;
    border-radius: 999px;
    font-size: .74rem;
    font-weight: 600;
}

.admin-pill-on {
    background: #dcfce7;
    color: #14532d;
}

.admin-pill-off {
    background: #e2e8f0;
    color: #334155;
}

.admin-pill-new {
    background: #ffedd5;
    color: #9a3412;
}

/* Audit-trail tones. Same palette as the pills above, kept in sync so the log
   table does not introduce a second visual language. */
.admin-pill-ok {
    background: #dcfce7;
    color: #14532d;
}

.admin-pill-bad {
    background: #fee2e2;
    color: #991b1b;
}

.admin-pill-warn {
    background: #fef3c7;
    color: #92400e;
}

/* Filter bar above a table: action, module, search. */
.admin-filter-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
    margin-bottom: 1rem;
}

.admin-filter-bar select,
.admin-filter-bar input[type="text"] {
    padding: .45rem .6rem;
    border: 1px solid var(--adm-border);
    border-radius: 8px;
    background: #fff;
    color: inherit;
    font-size: .85rem;
}

.admin-filter-bar input[type="text"] {
    flex: 1 1 14rem;
    min-width: 0;
}

.admin-filter-bar select:focus,
.admin-filter-bar input:focus {
    outline: 2px solid var(--adm-primary);
    outline-offset: 1px;
}

/* ----------------------------------------------------------------- forms */
.admin-form-row {
    margin-bottom: .9rem;
}

.admin-form-row label {
    display: block;
    margin-bottom: .3rem;
    font-size: .82rem;
    font-weight: 600;
}

.admin-form-row .help {
    color: var(--adm-muted);
    font-size: .76rem;
    margin-top: .25rem;
}

/*
 * Captcha: a short input next to a fixed-width generated image, with a refresh
 * control underneath. The image must not shrink, or the code becomes unreadable
 * and the login fails for a reason the user cannot see.
 */
.admin-captcha-row {
    display: flex;
    align-items: center;
    gap: .6rem;
}

.admin-captcha-row .form-control {
    flex: 1 1 auto;
    min-width: 0;
    /* Uppercase input, matching the alphabet the image is drawn from. */
    text-transform: uppercase;
    letter-spacing: .25em;
    font-weight: 600;
}

.admin-captcha-image {
    flex: 0 0 auto;
    width: 150px;
    height: 48px;
    border: 1px solid var(--adm-border);
    border-radius: 8px;
    background: #f5f6f8;
    user-select: none;
}

.admin-captcha-refresh {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    margin-top: .35rem;
    padding: 0;
    border: 0;
    background: none;
    color: var(--adm-muted);
    font-size: .76rem;
    cursor: pointer;
}

.admin-captcha-refresh:hover {
    color: var(--adm-primary);
}

.admin-captcha-refresh.is-spinning i {
    display: inline-block;
    animation: admin-captcha-spin .5s linear;
}

@keyframes admin-captcha-spin {
    from { transform: rotate(0); }
    to { transform: rotate(360deg); }
}

.admin-form-control,
.admin-form-row input[type="text"],
.admin-form-row input[type="email"],
.admin-form-row input[type="password"],
.admin-form-row textarea,
.admin-form-row select {
    width: 100%;
    padding: .5rem .65rem;
    border: 1px solid var(--adm-border);
    border-radius: 8px;
    background: #fff;
    font-size: .88rem;
    color: var(--adm-text);
}

.admin-form-row textarea {
    min-height: 90px;
    resize: vertical;
}

.admin-form-row input:focus,
.admin-form-row textarea:focus,
.admin-form-row select:focus {
    outline: 2px solid rgba(234, 88, 12, .35);
    border-color: var(--adm-primary);
}

.admin-form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 0 1rem;
}

.admin-error {
    color: #b91c1c;
    font-size: .78rem;
    margin-top: .2rem;
}

.admin-form-actions {
    display: flex;
    gap: .5rem;
    align-items: center;
    margin-top: 1.1rem;
    padding-top: 1rem;
    border-top: 1px solid var(--adm-border);
}

/* --------------------------------------------------------------- buttons */
.admin-btn-admin {
    border-radius: 8px;
    font-size: .85rem;
    padding: .45rem .85rem;
    background: var(--adm-primary);
    border-color: var(--adm-primary);
    color: #fff;
}

.admin-btn-admin:hover {
    background: #c2410c;
    color: #fff;
}

.admin-btn-admin.btn-outline-danger {
    background: transparent;
    border-color: #fca5a5;
    color: #b91c1c;
}

.admin-btn-admin.btn-outline-danger:hover {
    background: #fee2e2;
}

.admin-btn-secondary {
    display: inline-block;
    border: 1px solid var(--adm-border);
    border-radius: 8px;
    background: #fff;
    color: var(--adm-text);
    font-size: .85rem;
    padding: .45rem .85rem;
    text-decoration: none;
}

.admin-btn-secondary:hover {
    background: #f8fafc;
}

.admin-btn-link {
    color: var(--adm-primary);
    font-size: .82rem;
    text-decoration: none;
}

.admin-btn-link:hover {
    text-decoration: underline;
}

.admin-btn-link-danger {
    color: #dc2626;
    background: none;
    border: 0;
    cursor: pointer;
    font-size: .82rem;
    padding: 0;
}

.admin-empty {
    color: var(--adm-muted);
    font-size: .87rem;
    padding: .75rem 0;
}

/* ----------------------------------------------------------------- login */
.admin-login-wrap {
    min-height: 100vh;
    display: grid;
    place-items: center;
    background: linear-gradient(135deg, #0f172a, #9a3412);
    padding: 1.5rem;
}

.admin-login-card {
    width: 100%;
    max-width: 380px;
    background: #fff;
    border-radius: 14px;
    padding: 1.75rem;
    box-shadow: 0 18px 45px rgba(2, 6, 23, .3);
}

.admin-login-card h1 {
    margin: 0 0 .25rem;
    font-size: 1.2rem;
}

.admin-login-card .sub {
    color: var(--adm-muted);
    font-size: .83rem;
    margin-bottom: 1.1rem;
}

/* ------------------------------------------------------------ responsive */

/*
 * The hamburger control and the dismiss layer exist only at phone widths. They
 * are display:none above the breakpoint rather than hidden by JS, so a
 * wide-screen browser with JS disabled still gets the full sidebar.
 */
.admin-nav-toggle-btn {
    display: none;
}

.admin-nav-backdrop[hidden] {
    display: none;
}

@media (max-width: 820px) {
    /*
     * The sidebar is no longer stacked above the page. It slides in from the
     * left as an overlay so the content underneath stays where it was: with the
     * old column layout the nav pushed the page below the fold, and every
     * admin screen opened with a full screen of links instead of its content.
     */
    .admin-shell {
        flex-direction: column;
        align-items: stretch;
        max-width: 100%;
    }

    .admin-sidebar {
        /*
         * Fixed and translated off-canvas rather than display:none: a hidden
         * element is unreachable by Tab, so the links inside could not be
         * focused by keyboard before it was opened.
         */
        position: fixed;
        top: 0;
        left: 0;
        bottom: 0;
        z-index: 1045;
        width: min(80vw, 300px);
        flex: none;
        min-height: 0;
        margin: 0;
        padding: 1rem .6rem;
        overflow-y: auto;
        transform: translateX(-100%);
        transition: transform .22s ease;
        box-shadow: 0 0 24px rgba(0, 0, 0, .25);
    }

    /* The single class that opens it, toggled by the button. */
    .admin-body.admin-nav-open .admin-sidebar {
        transform: translateX(0);
    }

    /* Stop the page behind the menu from scrolling while it is open. */
    .admin-body.admin-nav-open {
        overflow: hidden;
    }

    .admin-nav-backdrop {
        position: fixed;
        inset: 0;
        z-index: 1040;
        background: rgba(15, 23, 42, .45);
    }

    .admin-nav-toggle-btn {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        flex: 0 0 auto;
        width: 44px;
        height: 44px;
        margin-right: .5rem;
        padding: 0;
        border: 0;
        border-radius: 8px;
        /* Solid brand orange rather than a bordered white box: on a phone the
           button is one of only two controls in the topbar, and a white square
           on a white header was easy to miss. No border keeps it from reading
           as an input. */
        background: var(--adm-primary);
        color: #fff;
        font-size: 1.6rem;
        line-height: 1;
        cursor: pointer;
    }

    .admin-nav-toggle-btn:hover {
        background: #c2410c;
    }

    .admin-nav-toggle-btn:active {
        transform: scale(.94);
    }

    .admin-nav-toggle-btn:focus-visible {
        outline: 2px solid var(--adm-primary);
        outline-offset: 2px;
    }

    /*
     * The brand is what gets squeezed on a phone, not the button: the button is
     * sized above and must not shrink, and the user chip plus the logout form
     * are the first things to give up room.
     */
    .admin-topbar {
        gap: .5rem;
    }

    .admin-topbar-right {
        gap: .5rem;
    }

    .admin-topbar-right .admin-link-muted,
    .admin-topbar-right .admin-user-chip {
        display: none;
    }

    /*
     * The nav stays a vertical list on phones. It used to be a horizontal
     * scroll strip, which put the expanded section panel inside a row of links:
     * the sub-groups were clipped to a narrow column and the rest had to be
     * scrolled sideways, so tapping the menu looked like nothing happened. A
     * plain block list gives the panel the full width.
     */
    .admin-sidebar nav {
        display: block;
        max-height: none;
    }

    .admin-nav-link {
        white-space: normal;
        margin-bottom: 0;
    }

    .admin-nav-panel {
        margin-left: .75rem;
        padding-left: .65rem;
    }

    /* The panel is a child of a flex row on wider screens; here it is stacked,
       so it must be allowed to take full width. */
    .admin-nav-group,
    .admin-nav-panel,
    .admin-subnav-group,
    .admin-subnav-items {
        width: 100%;
        min-width: 0;
        flex: 0 0 auto;
    }

    .admin-main {
        padding: 1rem;
    }

    /*
     * Two rules that fix horizontal overflow on the dashboard at phone widths.
     *
     * The shell is a column flex container, so `.admin-main` is a cross-axis
     * item: with `align-items:stretch` (the default) it should match the shell
     * width, but a wide descendant (a Bootstrap grid column, or a Chart.js
     * canvas measured before the viewport settled) makes the item's max-content
     * width larger than the shell, and the whole page then scrolls sideways.
     *
     * `max-width:100%` + `width:100%` pins the item to the container instead of
     * letting content dictate its width; `min-width:0` on the row children lets
     * the grid itself shrink so `.table-responsive` can scroll a table inside
     * its card rather than widening the page.
     */
    .admin-main {
        width: 100%;
        max-width: 100%;
    }

    .admin-main .row > [class*='col'] {
        min-width: 0;
    }
}

/* Honour a reduced-motion preference: the slide is decoration, not meaning. */
@media (prefers-reduced-motion: reduce) {
    .admin-sidebar {
        transition: none;
    }
}

/* ================================================================
   Content editor — section cards, repeatable rows, media library
   ================================================================ */

.admin-page-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    margin-bottom: 1.25rem;
}

.admin-h1 {
    font-size: 1.5rem;
    font-weight: 700;
    margin: 0;
    color: #14213d;
}

.admin-h1 .bi {
    color: #ea580c;
}

.admin-sub {
    color: #64748b;
    font-size: .9rem;
    margin: .35rem 0 0;
    max-width: 60ch;
}

/* sidebar sub-navigation */
.admin-subnav {
    display: none;
    margin: .15rem 0 .5rem;
    padding-left: .65rem;
    border-left: 2px solid #e2e8f0;
}

.admin-subnav.is-open {
    display: block;
}

/*
 * Collapsible group panel. The `hidden` attribute does the real work, so the
 * panel works with no JS at all; the JS only wires up the aria state and the
 * caret rotation.
 */
.admin-nav-panel {
    margin: .2rem 0 .5rem;
    padding-left: .65rem;
    /* Light-theme border (#e2e8f0) on the dark sidebar reads as nothing. */
    border-left: 2px solid rgba(255, 255, 255, .12);
}

.admin-nav-panel[hidden] {
    display: none;
}

.admin-nav-group-toggle {
    width: 100%;
    border: 0;
    background: transparent;
    text-align: left;
    cursor: pointer;
    font: inherit;
}

.admin-nav-group-toggle .admin-nav-caret {
    transition: transform .15s ease;
}

.admin-nav-group-toggle[aria-expanded='true'] .admin-nav-caret {
    transform: rotate(180deg);
}

/*
 * Second-level grouping inside the content panel.
 *
 * These used to carry the light-theme colours (#475569 on #f1f5f9), which is
 * dark text on a light chip — invisible against the dark sidebar they actually
 * sit in. The values below are tuned for the sidebar background instead.
 */
.admin-subnav-group-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    width: 100%;
    padding: .4rem .6rem;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: #a5b4fc;
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    text-align: left;
    cursor: pointer;
}

.admin-subnav-group-toggle:hover {
    background: rgba(255, 255, 255, .08);
    color: #c7d2fe;
}

.admin-subnav-group-toggle .bi {
    font-size: .7rem;
    transition: transform .15s ease;
}

.admin-subnav-group.is-open > .admin-subnav-group-toggle .bi {
    transform: rotate(90deg);
}

.admin-subnav-items {
    padding-left: .35rem;
}

.admin-subnav-items[hidden] {
    display: none;
}

.admin-subnav-link {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .38rem .6rem;
    border-radius: 6px;
    font-size: .84rem;
    color: #e2e8f0;
    text-decoration: none;
    line-height: 1.3;
}

.admin-subnav-link:hover {
    background: rgba(255, 255, 255, .1);
    color: #ffffff;
}

.admin-subnav-link.is-active {
    /* Orange tint: bright enough to read on the dark sidebar, still clearly
       "this one", and consistent with --adm-primary used by the top-level links. */
    background: rgba(234, 88, 12, .22);
    color: #fdba74;
    font-weight: 600;
    box-shadow: inset 2px 0 0 var(--adm-primary);
}

.admin-subnav-link .bi {
    font-size: .95rem;
    flex-shrink: 0;
}

.admin-nav-caret {
    margin-left: auto;
    font-size: .7rem;
    color: #94a3b8;
}

/* content section cards */
.content-card {
    border: 1px solid #e2e8f0;
    border-radius: 10px;
    transition: box-shadow .15s ease, transform .15s ease, border-color .15s ease;
}

.content-card:hover {
    box-shadow: 0 6px 18px rgba(15, 23, 42, .08);
    transform: translateY(-2px);
    border-color: #bfdbfe;
}

.content-card-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 42px;
    height: 42px;
    flex-shrink: 0;
    border-radius: 9px;
    background: #fff7ed;
    color: #ea580c;
    font-size: 1.2rem;
}

.content-card-title {
    font-size: 1rem;
    font-weight: 650;
    margin: 0 0 .2rem;
    color: #14213d;
}

.content-card-desc {
    font-size: .82rem;
    color: #64748b;
    margin: 0;
    line-height: 1.45;
}

/* repeatable rows */
.repeat-row {
    border: 1px solid #e2e8f0;
    border-radius: 9px;
    padding: .9rem 1rem .3rem;
    margin-bottom: .5rem;
    background: #fbfdff;
}

.repeat-row-head {
    display: flex;
    align-items: center;
    /*
     * A plain space-between with three children (number, optional "Nonaktif"
     * badge, buttons) pushes the badge into the middle of the row instead of
     * keeping it beside the number. An explicit gap plus margin-left:auto on
     * the button group holds the two ends apart and the badge next to the number
     * whether or not it is present.
     */
    gap: .5rem;
    margin-bottom: .6rem;
}

.repeat-row-head > .d-flex {
    margin-left: auto;
}

.repeat-row-flag {
    font-size: .72rem;
    font-weight: 600;
}

/*
 * A switched-off item stays in the form but reads as parked: dimmed border and
 * background, with the badge naming the state. Without this the row looks
 * identical to a live one and the item is simply missing from the site.
 */
.repeat-row.is-inactive {
    background: #f8fafc;
    border-color: #cbd5e1;
    border-style: dashed;
}

.repeat-row.is-inactive .repeat-row-num {
    background: #f1f5f9;
    color: #64748b;
}

.repeat-row-num {
    font-size: .8rem;
    font-weight: 700;
    color: #ea580c;
    background: #fff7ed;
    border-radius: 20px;
    padding: .15rem .6rem;
}

/*
 * Compact table (Product).
 *
 * Each item is two rows: a summary row for scanning, and a detail row holding
 * the real inputs. The pairing needs a real table — in a div layout the detail
 * panel would have to live inside the summary row.
 */
.compact-table {
    margin-bottom: 0;
}

.compact-table > thead th {
    font-size: .72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: #64748b;
    background: #f8fafc;
    border-bottom: 1px solid #e2e8f0;
    white-space: nowrap;
    padding: .55rem .75rem;
}

.compact-table > tbody > tr > td {
    padding: .45rem .75rem;
    vertical-align: middle;
}

.compact-row > td {
    /* Long names must not stretch the table off-screen; the full text is in the
     * title attribute and in the detail row. */
    max-width: 320px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.compact-row > td.compact-desc {
    /*
     * Deskripsi is a paragraph, so it wraps instead of ellipsising — a clipped
     * description is exactly what the admin came here to read. It gets its own
     * wide column and is allowed to grow to a couple of lines.
     */
    max-width: none;
    white-space: normal;
    line-height: 1.45;
    font-size: .84rem;
    color: #64748b;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.compact-row > td.compact-name {
    font-weight: 600;
    color: #0f172a;
    max-width: 260px;
}

/*
 * The action buttons were pushed out of view because the table could not shrink
 * below the sum of its columns' min-widths inside the card. `table-layout:
 * fixed` makes the declared widths authoritative and lets the ellipsis rules
 * above do their job; the container scrolls horizontally when even that is not
 * enough (see .compact-table's wrapper).
 */
.compact-table {
    table-layout: fixed;
    width: 100%;
}

.compact-table > thead th,
.compact-table > tbody > tr > td {
    overflow-wrap: break-word;
}

/* Nothing may overlap the action cell, whatever the content length. */
.compact-table > tbody > tr > td.compact-actions {
    white-space: nowrap;
    overflow: visible;
}

/*
 * Actions are labelled buttons, not bare icons.
 *
 * Icon-only controls were the complaint that started this: at 15x7px with a
 * 0x0 glyph they were unreadable and, to anyone not already sure what a
 * chevron means, indistinguishable from a layout glitch. Text makes each
 * control self-describing and gives it a hit target big enough to use.
 */
.compact-actions {
    display: flex;
    gap: .35rem;
    justify-content: flex-end;
    align-items: center;
    white-space: nowrap;
    overflow: visible;
}

.compact-actions .btn {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .25rem .55rem;
    font-size: .78rem;
    line-height: 1.4;
    /* An icon-only button collapses to its padding; floor the size so a glyph
     * that fails to load still leaves something clickable. */
    min-width: 2.4rem;
    min-height: 1.9rem;
}

/* The row expander is the "edit" affordance, so name it. */
.compact-expander {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    border: 0;
    background: none;
    padding: .25rem .4rem;
    color: #475569;
    font-size: .78rem;
    line-height: 1.4;
    cursor: pointer;
    white-space: nowrap;
}

.compact-expander:hover {
    color: var(--primary-color, #ea580c);
}

.compact-expander [data-expand-icon] {
    display: inline-block;
    transition: transform .15s ease;
    /* Without the icon sheet the glyph is a 0x0 box; keep a minimum so the
     * button still reads as having a marker. */
    min-width: .7rem;
}

.compact-expander[aria-expanded="true"] [data-expand-icon] {
    transform: rotate(90deg);
}

.compact-thumb {
    width: 40px;
    height: 40px;
    /* Crops to fill: at 40px a portrait photo is all crop anyway, and this is a
     * glanceable cue rather than the picture itself. The landing cards stay on
     * contain. */
    object-fit: cover;
    border-radius: 6px;
    border: 1px solid #e2e8f0;
    background: #f1f5f9;
    display: block;
}

.compact-thumb-empty {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: 6px;
    border: 1px dashed #cbd5e1;
    color: #cbd5e1;
    background: #f8fafc;
}

.compact-switch .form-check-input {
    cursor: pointer;
}

.compact-flag {
    display: none;
}

.compact-actions {
    white-space: nowrap;
}

.compact-actions .btn {
    padding: .15rem .4rem;
}

/*
 * A switched-off item stays in the table but reads as parked: dimmed row and a
 * struck-through name. The badge is always present in the markup and revealed
 * only for inactive rows, so toggling the switch does not have to insert or
 * remove a node.
 */
.compact-row.is-inactive > td {
    background: #f8fafc;
    color: #64748b;
}

.compact-row.is-inactive td:first-child ~ td .compact-flag,
.compact-row.is-inactive .compact-flag {
    display: inline-flex;
}

/* The detail row: a tinted panel spanning the full width of the table. */
.compact-detail > td {
    padding: 0;
    background: #fbfdff;
    border-bottom: 1px solid #e2e8f0;
}

.compact-detail.is-inactive > td {
    background: #f1f5f9;
}

.compact-detail-grid {
    padding: .85rem .75rem .35rem 2.1rem;
}

.compact-detail-grid > [class^="col-"] {
    /* The last field should not leave a ragged half-width gap. */
    margin-bottom: .5rem;
}

.compact-detail .form-text {
    font-size: .78rem;
}

@media (max-width: 767.98px) {
    /*
     * Below the md breakpoint the table scrolls horizontally rather than
     * wrapping into unreadable stacks — wrapping a table loses the column
     * alignment that makes it readable in the first place.
     */
    .compact-detail-grid {
        padding-left: .75rem;
    }
}

@media (prefers-reduced-motion: reduce) {
    .compact-expander [data-expand-icon] {
        transition: none;
    }
}

/*
 * The sticky save bar sits over the bottom of the form. Without this reserve
 * the last field in a detail panel ends up underneath it and cannot be
 * clicked: elementFromPoint returns the bar, not the input. The reserve is on
 * the form itself so it applies however many rows are open.
 */
#content-form:has(.compact-table) {
    padding-bottom: 4.5rem;
}

/* Solid, so nothing shows through the bar while scrolling past it. */
.admin-form-actions {
    position: sticky;
    bottom: 0;
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: .85rem 0;
    margin-top: 1rem;
    background: #fff;
    border-top: 1px solid #e2e8f0;
    backdrop-filter: blur(4px);
    z-index: 5;
}

/* image field + picker */
.media-field-preview {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 120px;
    border: 1px dashed #cbd5e1;
    border-radius: 8px;
    background: #f8fafc;
    padding: .5rem;
    margin-bottom: .5rem;
    overflow: hidden;
}

.media-field-preview.is-empty {
    background: #f1f5f9;
}

.media-field-preview img {
    max-width: 100%;
    max-height: 190px;
    object-fit: contain;
    border-radius: 5px;
}

.media-field-preview [data-media-clear] {
    position: absolute;
    top: .4rem;
    right: .4rem;
}

.media-field-placeholder {
    color: #94a3b8;
    font-size: .84rem;
}

.form-control-color {
    width: 52px;
    padding: .2rem;
    height: 38px;
}

/*
 * A blank colour field still has to render an <input type="color">, which cannot
 * be empty. The seeded swatch is a PLACEHOLDER, so it is muted and dashed to say
 * "no colour chosen" at a glance — the neutral grey alone was being read as a
 * real value. `cursor: pointer` is kept: clicking it is how a colour gets picked.
 */
.form-control-color.is-placeholder {
    opacity: .45;
    border-style: dashed;
    cursor: pointer;
}

.form-control-color.is-placeholder:hover,
.form-control-color.is-placeholder:focus {
    opacity: .8;
}

/* media library */
.media-filters {
    margin-bottom: 1.25rem;
}

.media-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
    gap: .9rem;
}

.media-card {
    border: 1px solid #e2e8f0;
    border-radius: 10px;
    background: #fff;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    transition: box-shadow .15s ease, border-color .15s ease;
}

.media-card:hover {
    box-shadow: 0 5px 16px rgba(15, 23, 42, .09);
    border-color: #bfdbfe;
}

.media-card-pick {
    cursor: pointer;
}

.media-card-pick:hover {
    border-color: #ea580c;
    background: #fff7ed;
}

/*
 * Multi-select state for the gallery picker.
 *
 * The selected state has to read at a glance across a grid of thumbnails, and it
 * has to survive the `:hover` rule above — a selected card that is merely being
 * pointed at must not look unselected. Two cues rather than one: a brand border
 * plus a check badge, because a colour change alone is easy to miss on the
 * smaller tiles.
 */
.media-card-pick.media-card-picked {
    border-color: #ea580c;
    box-shadow: 0 0 0 2px #ea580c inset;
    background: #fff7ed;
}

.media-card-pick.media-card-picked::after {
    content: '\f26e';
    /* Bootstrap Icons' check-circle, inlined so the badge needs no markup and
       cannot go missing if the icon font fails to load. */
    font-family: "bootstrap-icons";
    position: absolute;
    top: .4rem;
    right: .4rem;
    width: 1.5rem;
    height: 1.5rem;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: #ea580c;
    color: #fff;
    font-size: .85rem;
    line-height: 1;
}

/* The thumb wrapper needs to be the positioning context for the badge. */
.media-card-pick {
    position: relative;
}

.media-thumb {
    aspect-ratio: 4 / 3;
    background: #f1f5f9;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}

.media-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* A media row whose file is gone from disk. It used to render as an empty grey
   box indistinguishable from a slow-loading thumbnail, so the user could not
   tell a broken reference from a healthy one. */
.media-thumb-missing {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .35rem;
    padding: .5rem;
    text-align: center;
    color: #b45309;
    background: #fffbeb;
    border: 1px dashed #fcd34d;
}

.media-thumb-missing i {
    font-size: 1.5rem;
    opacity: .7;
}

.media-thumb-missing-text {
    font-size: .7rem;
    font-weight: 600;
    line-height: 1.25;
    color: #92400e;
}

.media-card-missing .media-card-title {
    color: #92400e;
}

.media-card-body {
    padding: .5rem .65rem;
    flex-grow: 1;
}

.media-card-title {
    font-size: .84rem;
    font-weight: 600;
    color: #1e293b;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.media-card-meta {
    font-size: .72rem;
    color: #94a3b8;
    margin-top: .15rem;
}

.media-card-actions {
    display: flex;
    gap: .3rem;
    padding: .5rem .65rem;
    border-top: 1px solid #f1f5f9;
    align-items: center;
}

.media-card-actions form {
    margin: 0;
}

.media-card-actions .btn {
    font-size: .75rem;
    padding: .2rem .45rem;
}

/*
 * Section order list (Pengaturan Global → Urutan Section).
 *
 * An <ol> whose visual numbering is drawn by the markup (.section-order-index)
 * rather than by list-style, because the numbers have to line up with a row that
 * is "pinned" and therefore not counted the same way a movable one is. The
 * counter is renumbered by JS after every move.
 */
.section-order-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: .5rem;
}

.section-order-item {
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: .65rem .85rem;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
    background: #fff;
}

/* Pinned rows are shown but must never move — see the reader in Settings.php. */
.section-order-item.is-pinned {
    background: #f8fafc;
    border-style: dashed;
}

.section-order-index {
    flex: 0 0 auto;
    width: 1.75rem;
    height: 1.75rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: #eff6ff;
    color: #1d4ed8;
    font-size: .8rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.section-order-item.is-pinned .section-order-index {
    background: #e2e8f0;
    color: #64748b;
}

.section-order-grip {
    color: #94a3b8;
    flex: 0 0 auto;
}

.section-order-pin {
    color: #64748b;
    flex: 0 0 auto;
}

.section-order-label {
    flex: 1 1 auto;
    min-width: 0;
    font-weight: 500;
}

.section-order-controls {
    display: flex;
    gap: .35rem;
    flex: 0 0 auto;
}

.section-order-note {
    flex: 0 0 auto;
}
