/* =====================================================================
   College Event Management ERP - application styles
   Layered on top of Bootstrap 5. Only what Bootstrap does not provide.
   ===================================================================== */

:root {
  --brand:        #0d3b66;
  --brand-dark:   #08294a;
  --brand-accent: #ffc300;
  --sidebar-w:    250px;
  --topbar-h:     56px;
}

body { font-family: 'Segoe UI', system-ui, -apple-system, 'Helvetica Neue', Arial, sans-serif; }

.bg-brand { background-color: var(--brand) !important; }
.text-brand { color: var(--brand) !important; }
/* Marks the chosen card in a set of alternatives — the cheer squad deals on
   the registration form. Bootstrap has no border-brand of its own. */
.border-brand { border-color: var(--brand) !important; box-shadow: inset 0 0 0 1px var(--brand); }
.btn-brand { background-color: var(--brand); border-color: var(--brand); color: #fff; }
.btn-brand:hover { background-color: var(--brand-dark); border-color: var(--brand-dark); color: #fff; }

/* ---------- Admin shell ---------------------------------------------- */

.admin-body { padding-top: var(--topbar-h); background: #f4f6f9; }

/*
  The sidebar is a Bootstrap responsive offcanvas: a permanent column from lg
  up, a slide-over panel below it.

  Below lg, Bootstrap owns position, transform and z-index, and overriding any
  of them breaks the panel. This used to set `position: fixed` and
  `z-index: 1030` at every width, which put the open panel BELOW the offcanvas
  backdrop (1040) instead of above it (1045). Tapping the hamburger dimmed the
  screen with the menu hidden behind the dim, and the next tap landed on the
  backdrop and closed it again — so the menu looked completely dead on a phone.

  Only the colours are set here, and through Bootstrap's own custom properties,
  so there is nothing to fight with !important. Desktop layout lives in the
  min-width block further down.
*/
.admin-sidebar {
  --bs-offcanvas-bg: var(--brand-dark);
  --bs-offcanvas-color: rgba(255, 255, 255, .88);
  --bs-offcanvas-width: min(82vw, var(--sidebar-w));
  --bs-offcanvas-border-width: 0;
}
.admin-sidebar .nav-link {
  color: rgba(255, 255, 255, .88);
  padding: .55rem 1.1rem;
  font-size: .9rem;
  border-left: 3px solid transparent;
  display: flex;
  align-items: center;
  gap: .6rem;
}
.admin-sidebar .nav-link:hover { color: #fff; background: rgba(255, 255, 255, .06); }
.admin-sidebar .nav-link.active {
  color: #fff;
  background: rgba(255, 255, 255, .1);
  border-left-color: var(--brand-accent);
  font-weight: 500;
}
.admin-sidebar .nav-section {
  color: rgba(255, 255, 255, .55);
  font-size: .68rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  padding: 1rem 1.1rem .3rem;
}

.admin-main { padding: 1.25rem; }

@media (min-width: 992px) {
  .admin-main { margin-left: var(--sidebar-w); padding: 1.5rem; }

  /* Above lg the offcanvas machinery switches off and the panel becomes an
     ordinary element, so it has to be positioned as the fixed column itself.
     Bootstrap also forces `background-color: transparent !important` here,
     which would erase the sidebar and leave white nav text on the light page
     behind it — hence the matching !important. */
  .admin-sidebar.offcanvas-lg {
    position: fixed;
    top: var(--topbar-h);
    left: 0;
    width: var(--sidebar-w);
    height: calc(100vh - var(--topbar-h));
    overflow-y: auto;
    z-index: 1030;
    background-color: var(--brand-dark) !important;
    visibility: visible !important;
    transform: none !important;
  }

  /* Bootstrap turns the body into a flex row above the breakpoint, which
     shrink-wraps the nav so the active-item highlight stops short of the
     sidebar edge. This is a vertical list; let it be a block. */
  .admin-sidebar.offcanvas-lg .offcanvas-body { display: block; }
}

/* ---------- KPI tiles ------------------------------------------------- */

.kpi { transition: transform .15s ease, box-shadow .15s ease; }
.kpi:hover { transform: translateY(-2px); box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .1) !important; }

.kpi-icon {
  font-size: 1.6rem;
  width: 48px; height: 48px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 12px;
  background: currentColor;
  flex-shrink: 0;
}
.kpi-icon i { color: #fff; }
.kpi-primary .kpi-icon { background: rgba(13, 110, 253, .12); }
.kpi-primary .kpi-icon i { color: #0d6efd; }
.kpi-warning .kpi-icon { background: rgba(255, 193, 7, .16); }
.kpi-warning .kpi-icon i { color: #d39e00; }
.kpi-success .kpi-icon { background: rgba(25, 135, 84, .12); }
.kpi-success .kpi-icon i { color: #198754; }
.kpi-danger  .kpi-icon { background: rgba(220, 53, 69, .12); }
.kpi-danger  .kpi-icon i { color: #dc3545; }

.kpi-value { font-size: 1.6rem; font-weight: 600; line-height: 1.1; color: #212529; }
.kpi-label { font-size: .78rem; color: #6c757d; text-transform: uppercase; letter-spacing: .04em; }

/* Brief highlight when a live figure changes */
@keyframes statBump {
  0%   { transform: scale(1);    color: inherit; }
  35%  { transform: scale(1.14); color: #0d6efd; }
  100% { transform: scale(1);    color: inherit; }
}
.stat-bump { animation: statBump .55s ease; display: inline-block; }

/* ---------- Live indicator -------------------------------------------- */

.live-dot {
  display: inline-block;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: currentColor;
  margin-right: .35rem;
  animation: pulse 2s infinite;
}
@keyframes pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: .3; }
}

@keyframes spin { to { transform: rotate(360deg); } }
.spin { display: inline-block; animation: spin .8s linear infinite; }

/* Respect users who asked for less motion */
@media (prefers-reduced-motion: reduce) {
  .live-dot, .spin, .stat-bump { animation: none !important; }
  .kpi { transition: none; }
}

/* ---------- Charts ---------------------------------------------------- */

.chart-wrap { position: relative; height: 280px; }
.chart-wrap.chart-sm { height: 220px; }

/* ---------- Activity feed --------------------------------------------- */

.activity-feed li {
  position: relative;
  padding: 0 0 .85rem 1.35rem;
  border-left: 2px solid #e9ecef;
}
.activity-feed li:last-child { border-left-color: transparent; padding-bottom: 0; }
.activity-feed .dot {
  position: absolute;
  left: -5px; top: .35rem;
  width: 8px; height: 8px;
  border-radius: 50%;
}

/* ---------- Package cards --------------------------------------------- */

.package-card { border: 2px solid transparent; transition: border-color .15s, box-shadow .15s; cursor: pointer; }
.package-card:hover { box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .1); }
.package-card.selected { border-color: var(--brand); box-shadow: 0 0 0 .2rem rgba(13, 59, 102, .12); }
.package-card .stars { color: var(--brand-accent); letter-spacing: .1em; }

/* ---------- Event picker ---------------------------------------------- */

.event-pick { border: 1px solid #dee2e6; border-radius: .5rem; padding: .75rem; transition: border-color .15s, background .15s; }
.event-pick:hover { border-color: #adb5bd; }
.event-pick.checked { border-color: var(--brand); background: rgba(13, 59, 102, .04); }
.event-pick.disabled { opacity: .55; background: #f8f9fa; cursor: not-allowed; }

.seat-bar { height: 5px; border-radius: 3px; }

/* ---------- QR scanner ------------------------------------------------ */

#qrReader { width: 100%; max-width: 420px; margin: 0 auto; border-radius: .5rem; overflow: hidden; }

.scan-verdict {
  border-radius: .75rem;
  padding: 1.25rem;
  text-align: center;
  animation: verdictIn .25s ease;
}
@keyframes verdictIn { from { opacity: 0; transform: scale(.96); } to { opacity: 1; transform: scale(1); } }
.scan-verdict.success   { background: #d1e7dd; color: #0a3622; border: 2px solid #a3cfbb; }
.scan-verdict.duplicate { background: #fff3cd; color: #664d03; border: 2px solid #ffe69c; }
.scan-verdict.error     { background: #f8d7da; color: #58151c; border: 2px solid #f1aeb5; }
.scan-verdict .verdict-icon { font-size: 2.75rem; line-height: 1; }

/* ---------- QR ID card ------------------------------------------------ */

.id-card {
  /* Fixed at 340px this overflowed a 360px phone once container padding was
     added, giving the whole page a horizontal scroll. */
  width: 100%;
  max-width: 340px;
  margin: 0 auto;
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 0 .5rem 1.5rem rgba(0, 0, 0, .18);
  background: #fff;
}
.id-card-header { background: var(--brand); color: #fff; padding: .9rem 1rem; text-align: center; }
.id-card-body { padding: 1rem; text-align: center; }
.id-card-qr { width: 190px; height: 190px; margin: 0 auto .75rem; }
.id-card-footer { background: #f8f9fa; padding: .5rem; text-align: center; font-size: .7rem; color: #6c757d; }

/* ---------- Print ----------------------------------------------------- */

@media print {
  .navbar, .admin-sidebar, .btn, .no-print, footer, .alert { display: none !important; }
  .admin-main, body { margin: 0 !important; padding: 0 !important; background: #fff !important; }
  .card { border: 1px solid #dee2e6 !important; box-shadow: none !important; }
  .id-card { box-shadow: none; border: 1px solid #dee2e6; }
  a[href]::after { content: none !important; }
}

/* ---------- Misc ------------------------------------------------------ */

.min-w-0 { min-width: 0; }
.table thead th { font-size: .78rem; text-transform: uppercase; letter-spacing: .03em; color: #6c757d; font-weight: 600; }
.hero { background: linear-gradient(135deg, var(--brand) 0%, var(--brand-dark) 100%); color: #fff; }

/* Sticky only while it is a side column. Once it stacks underneath the form
   there is nothing to stick alongside, and pinning it just makes the page
   jump as you scroll past the end of the events list. */
.sticky-summary { position: static; }
@media (min-width: 992px) {
  .sticky-summary { position: sticky; top: 80px; }
}

/* A tier at its quota: remaining options are visibly locked, not just disabled */
.event-pick.tier-locked { opacity: .45; background: #f8f9fa; cursor: not-allowed; }
.event-pick.tier-locked .form-check-label { text-decoration: line-through; }

/* ---------- Category blocks (Event Tiers screen) ---------------------- */
/* Two-level grouping: CONTINGENT contains the star tiers; NON CONTINGENT
   is its sibling. The nesting is load-bearing, so make it visible. */

.category-block {
  border-radius: .6rem;
  overflow: hidden;
  box-shadow: 0 .125rem .35rem rgba(0, 0, 0, .06);
}

.category-head {
  background: var(--brand);
  padding: .9rem 1.1rem;
}
.category-block--muted .category-head { background: #495057; }

.category-body {
  background: #fff;
  border: 1px solid #e9ecef;
  border-top: 0;
  padding: .35rem;
}

/* Star tiers sit inside the Contingent block, visibly indented. */
.tier-block {
  border: 1px solid #e9ecef;
  border-radius: .45rem;
  margin: .55rem;
  margin-left: 1.6rem;          /* the indent that shows the nesting */
  position: relative;
  overflow: hidden;
}
.tier-block::before {           /* connector back to the parent block */
  content: '';
  position: absolute;
  left: -0.85rem;
  top: 1.15rem;
  width: .85rem;
  height: 2px;
  background: #ced4da;
}
.tier-block::after {            /* vertical spine down the left */
  content: '';
  position: absolute;
  left: -0.85rem;
  top: -0.55rem;
  bottom: 50%;
  width: 2px;
  background: #ced4da;
}
.tier-block:first-of-type::after { top: -0.2rem; }

.tier-head {
  background: #f8f9fa;
  padding: .55rem .8rem;
  border-bottom: 1px solid #e9ecef;
}

@media (max-width: 767.98px) {
  .tier-block { margin-left: .55rem; }
  .tier-block::before, .tier-block::after { display: none; }
}

/* ---------------------------------------------------------------------
   Inline content editing.

   Only rendered for an administrator who asked for edit mode with
   ?cms=edit, so none of this ever reaches a visitor. The outline is
   deliberately faint until hovered: the point is to find a block, not to
   redecorate the page while you read it.
   --------------------------------------------------------------------- */
.cms-editable {
  position: relative;
  outline: 1px dashed rgba(13, 59, 102, .35);
  outline-offset: 4px;
  border-radius: 2px;
  transition: outline-color .15s ease;
}
.cms-editable:hover { outline-color: rgba(13, 59, 102, .9); }

.cms-pencil {
  position: absolute;
  top: -.75rem;
  right: -.75rem;
  z-index: 5;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  font-size: .7rem;
  color: #fff;
  background: #0d3b66;
  border-radius: 50%;
  text-decoration: none;
  opacity: .55;
}
.cms-editable:hover .cms-pencil { opacity: 1; }

.cms-bar {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  z-index: 1050;
  display: flex;
  gap: .4rem;
  align-items: center;
  padding: .45rem .7rem;
  font-size: .82rem;
  color: #fff;
  background: #0d3b66;
  border-radius: 2rem;
  box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .25);
}
.cms-bar a { color: #ffd166; text-decoration: none; }
.cms-bar a:hover { text-decoration: underline; }

/* Banner caption: legible over any photograph, without darkening the whole
   image the way a full overlay does. */
.banner-caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 1.5rem 1rem .75rem;
  color: #fff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, .8);
  background: linear-gradient(to top, rgba(0, 0, 0, .65), rgba(0, 0, 0, 0));
  border-bottom-left-radius: var(--bs-border-radius);
  border-bottom-right-radius: var(--bs-border-radius);
}
.carousel-inner .banner-caption { border-radius: 0; }

/* ---------------------------------------------------------------------
   Gallery grid and lightbox.
   --------------------------------------------------------------------- */
.gallery-tile {
  background: none;
  border: 0;
  cursor: zoom-in;
  overflow: hidden;
  transition: transform .15s ease, box-shadow .15s ease;
}
.gallery-tile:hover,
.gallery-tile:focus-visible { transform: translateY(-2px); }
.gallery-tile:focus-visible { outline: 2px solid var(--bs-primary, #0d3b66); outline-offset: 2px; }

/* A fixed ratio keeps the grid from going ragged when photos are a mix of
   portrait and landscape. The full image is shown in the lightbox. */
.gallery-thumb {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  display: block;
}
.gallery-thumb-video {
  background: #212529;
  color: rgba(255, 255, 255, .8);
}

.gallery-full {
  max-height: 78vh;
  width: auto;
}

.gallery-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  font-size: 1.35rem;
  color: #fff;
  background: rgba(0, 0, 0, .45);
  border: 0;
  border-radius: 50%;
  opacity: .8;
}
.gallery-nav:hover { opacity: 1; background: rgba(0, 0, 0, .7); }
.gallery-prev { left: .5rem; }
.gallery-next { right: .5rem; }

@media (max-width: 575.98px) {
  .gallery-nav { width: 2.25rem; height: 2.25rem; font-size: 1.1rem; }
}

/* Before/after panes in the website activity log. A long block of copy must
   not push the table sideways, so each pane scrolls on its own. */
.cms-diff {
  max-height: 11rem;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-word;
  font-size: .8rem;
}
.cms-diff-old { border-left: 3px solid var(--bs-danger, #dc3545) !important; }
.cms-diff-new { border-left: 3px solid var(--bs-success, #198754) !important; }

/* ---------------------------------------------------------------------
   Registration summary: what is still missing.
   --------------------------------------------------------------------- */
.picker-gap { cursor: pointer; }
.picker-gap:hover,
.picker-gap:focus-visible { background: rgba(0, 0, 0, .06); }
.picker-gap:focus-visible { outline: 2px solid var(--bs-warning, #ffc107); outline-offset: 1px; }

/* Flash the tier a shortfall points at, so the jump is visible rather than
   just leaving the reader somewhere new on the page. */
.tier-flash {
  animation: tier-flash 1.6s ease-out;
  border-radius: .5rem;
}
@keyframes tier-flash {
  0%, 40% { box-shadow: 0 0 0 .35rem rgba(255, 193, 7, .55); }
  100%    { box-shadow: 0 0 0 .35rem rgba(255, 193, 7, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .tier-flash { animation: none; box-shadow: 0 0 0 .35rem rgba(255, 193, 7, .55); }
}

/* A field or section the page has just scrolled to. Brief, so it draws the
   eye on arrival without sitting there decorating the form. */
.field-flash {
  animation: field-flash 1.6s ease-out;
  border-radius: .4rem;
}
@keyframes field-flash {
  0%, 40% { box-shadow: 0 0 0 .25rem rgba(220, 53, 69, .45); }
  100%    { box-shadow: 0 0 0 .25rem rgba(220, 53, 69, 0); }
}

/* Submit while requirements are outstanding: reads as unavailable but stays
   clickable, so the tap can be caught and explained. */
.btn-not-ready {
  filter: grayscale(1);
  opacity: .65;
}
.btn-not-ready:hover { opacity: .8; }

@media (prefers-reduced-motion: reduce) {
  .field-flash { animation: none; box-shadow: 0 0 0 .25rem rgba(220, 53, 69, .45); }
}

/* ---------------------------------------------------------------------
   Contact directory: names and numbers, edited as one HTML block.
   Styled here so the block stays simple headings and lists rather than
   markup an editor has to keep intact.
   --------------------------------------------------------------------- */
.contact-directory h3 {
  font-size: .8rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--bs-secondary-color, #6c757d);
  margin: 1.25rem 0 .4rem;
}
.contact-directory h3:first-child { margin-top: 0; }

.contact-directory ul {
  list-style: none;
  padding: 0;
  margin: 0;
}
.contact-directory li {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: .3rem 0;
  border-bottom: 1px solid rgba(0, 0, 0, .06);
}
.contact-directory li:last-child { border-bottom: 0; }
.contact-directory a { text-decoration: none; white-space: nowrap; }
.contact-directory a:hover { text-decoration: underline; }

/* ---------------------------------------------------------------------
   Contingent tier cards on the home page.

   The card is a <button> so it is reachable by keyboard and announced as
   expandable; these rules undo the browser's button chrome so it still
   looks like the bordered panel it replaced.
   --------------------------------------------------------------------- */
.tier-card {
  background: #fff;
  color: inherit;
  cursor: pointer;
  transition: background-color .15s ease, border-color .15s ease;
}

.tier-card:hover { background: rgba(0, 0, 0, .03); }

.tier-card:focus-visible {
  outline: 2px solid var(--brand-accent, #ffc107);
  outline-offset: 2px;
}

/* "Show events" / "Hide events" swap on the open card. */
.tier-card .tier-card-hide { display: none; }
.tier-card[aria-expanded="true"] .tier-card-hide { display: inline; }
.tier-card[aria-expanded="true"] .tier-card-show { display: none; }

.tier-card-chevron {
  display: inline-block;
  transition: transform .2s ease;
}

.tier-card[aria-expanded="true"] .tier-card-chevron { transform: rotate(180deg); }

@media (prefers-reduced-motion: reduce) {
  .tier-card,
  .tier-card-chevron { transition: none; }
}
