/* FILE: v2/assets/css/mareos-ui.css
 *
 * Sistem reka bentuk MAREOS. Dimuatkan oleh header.php, jadi SETIAP halaman
 * dalam kesemua sembilan portal melihatnya.
 *
 * Token diisytiharkan DI SINI dan hanya di sini. portal-sidebar.css membaca
 * token ini; ia tidak mengisytiharkan satu pun. Token yang diskop kepada
 * pembalut atau kepada stylesheet yang hanya sebahagian halaman memuatkan akan
 * gagal SENYAP -- var() menjadi tidak tertakrif, latar tidak terpakai, warna
 * teks literal tetap terpakai, dan hasilnya teks putih atas latar putih tanpa
 * sebarang ralat. Itu sudah berlaku sekali dalam projek ini (Pelajaran #25).
 *
 * Warna disampel dari v2/assets/img/letterhead1.png -- kepala surat rasmi NAAM.
 * Jangan reka warna jenama. Tiada aset World Archery dalam repo ini.
 */

:root {
    /* Taip */
    --font-ui: Inter, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-display: Chivo, var(--font-ui);

    /* Tajuk halaman -- SATU nilai untuk kesemua 107 halaman.
       Ia menggantikan h2/h3/h4/h5/h6 yang bercampur sebagai tajuk. */
    --page-title-size: 1.5rem;
    --page-title-weight: 600;

    /* Aksen: satu-satunya token yang berubah mengikut peranan.
       Lalai = royal NAAM, warna paling dominan pada kepala surat.
       --accent-ink lalai di sini untuk halaman yang tidak membawa kelas
       body.theme-* langsung; setiap peranan menetapkan semula sendiri di
       bawah -- lihat nota di situ tentang KENAPA ia tidak boleh diwarisi
       daripada :root sahaja. */
    --accent: #004090;
    --accent-ink: #ffffff;
    --accent-soft: #eef3fb;

    /* Neutral -- sama di setiap peranan */
    --ink: #0f172a;
    --muted: #64748b;
    --line: #e2e8f0;
    --surface: #ffffff;
    --surface-2: #f8fafc;

    /* Status. Merah dan emas kris TIDAK pernah menjadi aksen peranan --
       merah bermaksud tutup dalam 7 hari, emas bermaksud kursus. Satu warna
       tidak boleh membawa dua makna pada kad yang sama.
       Diisytiharkan di sini walaupun Pelan A tidak menggunakannya: Pelan E
       (modal) dan kad acara memerlukannya, dan token status yang dicipta
       kemudian akan dicipta dua kali dengan nilai berbeza. */
    --status-urgent: #b02040;
    --status-course: #f0c060;

    /* Satu skala radius */
    --radius-sm: 6px;
    --radius: 10px;
    --radius-lg: 16px;

    /* Bayang rendah. Bayang lalai Bootstrap ialah sebab besar kenapa
       antaramuka Bootstrap nampak seperti templat. */
    --shadow-sm: 0 1px 2px rgba(15, 23, 42, .06);
    --shadow: 0 2px 8px rgba(15, 23, 42, .08);

    /* Satu skala ruang */
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 24px;
    --space-6: 32px;
}

/* Aksen mengikut peranan. Kelima-lima biru disampel dari kepala surat.
   Paling gelap = autoriti paling luas; paling cerah = pengguna paling ramai.

   --accent-ink (teks ATAS latar aksen, guna .page-action) ditetapkan semula
   di sini kerana bukan setiap aksen peranan boleh dibaca dengan teks putih.
   Diukur terhadap teks putih vs --ink #0f172a:
     navy #002060: putih 15.27, --ink 1.17     -> putih
     royal #004090: putih 9.83, --ink 1.82      -> putih
     mid #0060a0: putih 6.60, --ink 2.70        -> putih
     azure #1090d0: putih 3.55 GAGAL, --ink 5.03 -> --ink
     sky #20b0f0: putih 2.46 GAGAL, --ink 7.25   -> --ink
     slate #475569: putih 7.58, --ink 2.36       -> putih */
body.theme-admin     { --accent: #002060; --accent-ink: #ffffff; }
body.theme-national  { --accent: #004090; --accent-ink: #ffffff; }
body.theme-organizer { --accent: #0060a0; --accent-ink: #ffffff; }
body.theme-club      { --accent: #1090d0; --accent-ink: var(--ink); }
body.theme-archer    { --accent: #20b0f0; --accent-ink: var(--ink); }

/* Empat portal pakar berkongsi slate dan dibezakan oleh lencana label pada
   sidebar, bukan oleh warna -- kerana merah dan emas sudah membawa makna
   status dan tidak boleh dipinjam sebagai warna peranan. */
body.theme-judge,
body.theme-pld,
body.theme-pic_judge,
body.theme-pic_coach { --accent: #475569; --accent-ink: #ffffff; }

body {
    font-family: var(--font-ui);
    color: var(--ink);
    background: var(--surface-2);
}

/* Digit berbaris menegak dalam lajur. 62 fail menghidangkan DataTables penuh
   markah, tarikh, yuran dan kuota yang orang banding baris ke baris -- ini
   kebolehbacaan, bukan kemasan. */
table,
.table,
.dataTable {
    font-variant-numeric: tabular-nums;
}

/* Pengepala halaman. SATU bentuk, menggantikan 32. */
.page-header {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    padding-bottom: var(--space-4);
    margin-bottom: var(--space-5);
    border-bottom: 1px solid var(--line);
}

.page-header-main {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    min-width: 0;
}

.page-header-icon {
    font-size: 1.25rem;
    line-height: 1.6;
    color: var(--accent);
    flex: none;
}

/* Saiz visual dipisahkan daripada aras heading, jadi tajuk boleh kelihatan
   sederhana tanpa menjadi h5 dan merosakkan susunan heading. */
.page-title {
    font-family: var(--font-display);
    font-size: var(--page-title-size);
    font-weight: var(--page-title-weight);
    line-height: 1.25;
    color: var(--ink);
    margin: 0;
}

.page-sub {
    font-size: .875rem;
    color: var(--muted);
    margin: var(--space-1) 0 0;
}

.page-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.page-action {
    background: var(--accent);
    border: 1px solid var(--accent);
    color: var(--accent-ink);
    border-radius: var(--radius-sm);
}

.page-action:hover,
.page-action:focus {
    filter: brightness(1.08);
    color: var(--accent-ink);
}

.page-action-outline {
    background: transparent;
    color: var(--accent);
}

.page-action-outline:hover,
.page-action-outline:focus {
    background: var(--accent-soft);
    color: var(--accent);
}

@media (max-width: 575.98px) {
    .page-header { gap: var(--space-3); }
    .page-actions { width: 100%; }
    .page-action { flex: 1 1 auto; text-align: center; }
}


/* ==========================================================================
   DASHBOARD COMPONENTS
   ==========================================================================

   Written as portal-neutral components, not archer-specific ones: the archer
   dashboard is the first caller, and Plan D gives organizer, national and club
   the same shapes. Every colour here reads a token, so a role's accent carries
   through without any per-portal rule.

   The tone modifiers (.is-alert etc.) are a WHITELIST in render_stat_tiles()
   and render_action_list() -- an unknown tone is dropped rather than printed
   into the class attribute. */

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

.stat-tile {
    display: flex;
    flex-direction: column;
    gap: .15rem;
    padding: 1rem 1.1rem;
    background: var(--surface, #fff);
    border: 1px solid var(--line, #e5e7eb);
    border-left: 3px solid var(--accent);
    border-radius: .6rem;
    text-decoration: none;
    color: inherit;
}

/* Only the interactive tiles move. A hover transform on a plain <div> tells
   someone it is clickable when it is not. */
a.stat-tile { transition: border-color .15s ease, box-shadow .15s ease; }
a.stat-tile:hover,
a.stat-tile:focus-visible {
    border-color: var(--accent);
    box-shadow: 0 2px 10px rgba(0, 0, 0, .06);
}

.stat-tile-label {
    font-size: .68rem;
    font-weight: 700;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--muted, #64748b);
}

/* Tabular figures so a column of counts lines up digit for digit; the UI font
   is already loaded with them for the DataTables pages. */
.stat-tile-value {
    font-size: 1.9rem;
    font-weight: 700;
    line-height: 1.1;
    color: var(--ink, #0f172a);
    font-variant-numeric: tabular-nums;
}

.stat-tile-sub {
    font-size: .78rem;
    color: var(--muted, #64748b);
}

/* Alert is the one tile allowed to shout, and only when its value is non-zero
   -- the caller decides, so "0 unpaid" stays calm instead of glowing red. */
.stat-tile.is-alert { border-left-color: #b02040; }
.stat-tile.is-alert .stat-tile-value { color: #b02040; }
.stat-tile.is-muted { border-left-color: var(--line, #e5e7eb); }

/* ---- panels ---- */

.dash-panel {
    background: var(--surface, #fff);
    border: 1px solid var(--line, #e5e7eb);
    border-radius: .6rem;
    overflow: hidden;
}

.dash-panel-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    padding: .85rem 1.1rem;
    border-bottom: 1px solid var(--line, #e5e7eb);
}

.dash-panel-title {
    margin: 0;
    font-size: .95rem;
    font-weight: 700;
    color: var(--ink, #0f172a);
}

.dash-panel-link {
    font-size: .78rem;
    font-weight: 600;
    color: var(--accent);
    text-decoration: none;
    white-space: nowrap;
}
.dash-panel-link:hover { text-decoration: underline; }

.dash-panel-body { padding: .35rem 0; }

/* ---- action list ---- */

.action-item {
    display: flex;
    gap: .75rem;
    align-items: flex-start;
    padding: .7rem 1.1rem;
    text-decoration: none;
    color: inherit;
    border-left: 3px solid transparent;
}
.action-item:hover { background: var(--surface-2, #f6f7f9); }

.action-item + .action-item { border-top: 1px solid var(--line, #e5e7eb); }

.action-item-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    margin-top: .42rem;
    flex: 0 0 8px;
    background: var(--muted, #64748b);
}

.action-item.is-alert .action-item-dot { background: #b02040; }
.action-item.is-warn  .action-item-dot { background: #f0c060; }
.action-item.is-info  .action-item-dot { background: var(--accent); }

.action-item-title  { font-size: .86rem; font-weight: 600; color: var(--ink, #0f172a); }
.action-item-detail { font-size: .78rem; color: var(--muted, #64748b); }

/* An empty state is an answer, not a blank box. */
.dash-empty {
    padding: 1.4rem 1.1rem;
    text-align: center;
    font-size: .84rem;
    color: var(--muted, #64748b);
}

/* ---- recent entries table ---- */

.dash-table { width: 100%; margin: 0; font-size: .84rem; }
.dash-table th {
    font-size: .66rem;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--muted, #64748b);
    padding: .5rem 1.1rem;
    border-bottom: 1px solid var(--line, #e5e7eb);
    white-space: nowrap;
}
.dash-table td {
    padding: .7rem 1.1rem;
    border-bottom: 1px solid var(--line, #e5e7eb);
    vertical-align: top;
}
.dash-table tr:last-child td { border-bottom: 0; }
.dash-table .event-title { font-weight: 600; color: var(--ink, #0f172a); }

/* Generic meta-text class -- not scoped to .dash-table. Event cards outside
   a table (organizer, and national/club to follow) need the same muted,
   smaller text as the recent-entries table's meta column. */
.event-meta { font-size: .76rem; color: var(--muted, #64748b); }

.pay-chip {
    display: inline-block;
    font-size: .66rem;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    padding: .2em .6em;
    border-radius: 50rem;
    white-space: nowrap;
}
.pay-chip.is-paid   { background: #e6f4ea; color: #14532d; }
.pay-chip.is-unpaid { background: #fbe9ed; color: #b02040; }
.pay-chip.is-free   { background: var(--surface-2, #f1f5f9); color: var(--muted, #64748b); }

/* Two columns on desktop, stacked below. The recent-entries table is the wider
   of the two, so it takes the larger share rather than an even split. */
.dash-split {
    display: grid;
    grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
    gap: 1rem;
    align-items: start;
}

@media (max-width: 991.98px) {
    .dash-split { grid-template-columns: 1fr; }
    .stat-tile-value { font-size: 1.6rem; }
    /* The table is the one thing here that cannot reflow below a certain
       width, so it scrolls on its own instead of widening the document. */
    .dash-table-wrap { overflow-x: auto; }
}

/* ---- quick links row ----
   A deliberately lighter tile than .stat-tile. These four sit ABOVE the
   summary now, so they are the first thing on the page and must not out-shout
   the numbers underneath: icon and label on one line, no large figure, and
   roughly half the vertical weight of a stat tile. */

.dash-section-title {
    font-size: .68rem;
    font-weight: 800;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--muted, #64748b);
    margin: 0 0 .6rem;
}

.quick-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: .6rem;
    margin-bottom: 1.5rem;
}

.quick-tile {
    display: flex;
    align-items: center;
    gap: .65rem;
    padding: .6rem .75rem;
    background: var(--surface, #fff);
    border: 1px solid var(--line, #e5e7eb);
    border-radius: .55rem;
    text-decoration: none;
    color: inherit;
    transition: border-color .15s ease, box-shadow .15s ease;
}

.quick-tile:hover,
.quick-tile:focus-visible {
    border-color: var(--accent);
    box-shadow: 0 2px 10px rgba(0, 0, 0, .06);
}

.quick-tile-icon {
    flex: 0 0 30px;
    width: 30px;
    height: 30px;
    border-radius: .45rem;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: .8rem;
    background: var(--accent);
    color: var(--accent-ink);
}

.quick-tile-text { min-width: 0; }

.quick-tile-label {
    display: block;
    font-size: .82rem;
    font-weight: 700;
    color: var(--ink, #0f172a);
    line-height: 1.25;
}

.quick-tile-sub {
    display: block;
    font-size: .72rem;
    color: var(--muted, #64748b);
    line-height: 1.25;
    /* One line only. These are signposts, not descriptions -- the full
       explanation lives on the page each one links to. */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Gold count badges on the archer event links. Reads --status-course rather
   than declaring a second token with the same value -- the comment beside that
   token warns about exactly this. Gold was the user's explicit request
   (17 Sep 2026). The token's reserved "kursus" meaning is declared but consumed
   NOWHERE in v2/ today, so nothing collides; whoever implements Plan E decides
   then whether the two meanings can coexist.
   Always paired with text-dark, never text-white: gold behind white text fails
   WCAG AA. */
.bg-gold { background-color: var(--status-course); }

/* Shared by the four archer list pages (Senarai group). Lived inline in
   list_judge.php until Task 8 moved it here, when the other three adopted
   the same filter card. */
.x-small { font-size: 0.75rem; }

/* Event cards. The register button used to carry `w-100 w-md-auto`, and
   w-md-auto is not a Bootstrap class -- Bootstrap 5.3 has no responsive width
   utilities at all, so w-100 won unconditionally and the button rendered as a
   full-width bar on desktop too. This is the media query that was meant.
   Density is a DESKTOP decision: below 992px the buttons are touch targets.

   The wrapper rule below is NOT redundant with the button rule -- do not
   "simplify" it away. `.btn-daftar { width: 100% }` alone resolves against
   its containing block, which is `.btn-daftar-wrap` (the `ms-auto` div): a
   shrink-to-fit box that hugs the button's own width. So `100%` meant
   "100% of the button's own width" and painted nothing -- the same failure
   mode as the `w-md-auto` bug this file exists to fix: CSS that reads
   correctly, raises no error, and does nothing. The wrapper must stretch
   first so the button's 100% has something real to resolve against. */
@media (max-width: 767.98px) {
  .event-actions { row-gap: .5rem; }
  .event-actions .btn-daftar-wrap { width: 100%; }
  .event-actions .btn-daftar { width: 100%; }
}

/* Registration rail (v2/archer/daftar.php). Sticky only while the rail fits --
   a rail taller than the viewport scrolls and the submit button can fall
   below the fold, the same failure the sidebar Log Keluar had before the
   17 Sep density pass. Measure the document against the viewport; a
   screenshot will not show it.
   The token below is --topbar-height (portal-sidebar.css :root, 48px, since
   the 18 Sep topbar replaced the old full-width navbar) -- it is what
   .portal-topbar and .portal-sidebar already use to size and position
   themselves, so it is a measured value already proven correct, not a guess. */
.daftar-rail { position: sticky; top: calc(var(--topbar-height, 48px) + 1rem); }

@media (max-width: 991.98px) {
  /* Below lg the rail returns to normal flow BELOW the form, so Hantar stays
     the last thing read on a phone. */
  .daftar-rail { position: static; top: auto; }
}

/* Public navbar. It carries no role, so it reads the :root accent -- royal NAAM.
   Bootstrap's own text-primary blue and btn-success green were never in the
   palette, and green in particular collided with nothing: gold already means
   "kursus" and crimson means "closes within 7 days". */
.navbar-brand-naam { color: var(--accent); }
.navbar-brand-naam:hover { color: var(--accent); }

.btn-naam {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--accent-ink);
}
.btn-naam:hover {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--accent-ink);
    filter: brightness(1.1);
}

.btn-outline-naam {
    border-color: var(--accent);
    color: var(--accent);
    background: transparent;
}
.btn-outline-naam:hover {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--accent-ink);
}
