/* Bridge layer between the legacy AdminLTE stylesheets and the React shell.
   Only rules that AdminLTE's jQuery (app.min.js / slimScroll) used to apply at
   runtime live here — visual design stays 100% in the mirrored legacy CSS. */

/* React mounts inside #root; make it transparent to the AdminLTE layout tree. */
#root { height: 100%; }

/* AdminLTE JS measured the window and set these inline; reproduce with CSS. */
.wrapper { min-height: 100vh; }
.content-wrapper { min-height: calc(100vh - 101px); }

/* slimScroll used to size the sidebar; plain overflow gives the same effect. */
.main-sidebar .sidebar {
  height: calc(100vh - 50px);
  overflow-y: auto;
  overflow-x: hidden;
}

/* Sidebar collapse animation parity when toggled from React state. */
.sidebar-mini.sidebar-collapse .content-wrapper,
.sidebar-mini.sidebar-collapse .main-footer {
  margin-left: 0 !important;
}

/* Bootstrap modals driven by React state (no jQuery): the .in state. */
.modal.in { display: block; }
.modal-backdrop.in { opacity: 0.5; }
body.modal-open { overflow: hidden; }

/* Dropdowns driven by React state keep Bootstrap's .open contract. */
.dropdown.open > .dropdown-menu { display: block; }

/* Clickable affordances that were <a href="#"> in PHP. */
.sidebar-toggle, .treeview > a, [role='button'] { cursor: pointer; }

/* Login page: the legacy usertemplate body background (scoped). */
.login-page {
  min-height: 100vh;
  background: #f7f7f7;
  font-family: 'Roboto', sans-serif;
}

/* Keep pagination/count strip of legacy DataTables look for React tables. */
.dataTables_wrapper .dataTables_paginate .paginate_button.current {
  cursor: default;
}

/* NProgress-style top loading bar rendered by React during API calls. */
.mynl-busy-bar {
  position: fixed;
  top: 0;
  left: 0;
  height: 2px;
  width: 100%;
  z-index: 10310;
  background: transparent;
  pointer-events: none;
}
.mynl-busy-bar .bar {
  height: 100%;
  width: 40%;
  background: #29d;
  box-shadow: 0 0 10px #29d, 0 0 5px #29d;
  animation: mynl-busy-slide 1.2s ease-in-out infinite;
}
@keyframes mynl-busy-slide {
  0% { margin-left: -40%; }
  100% { margin-left: 100%; }
}

/* Legacy `style-main.css` carries an UNSCOPED `input[type="file"] { opacity: 0 }`.
   In the old app that was harmless: every file input sat inside a `.btn.btn-file`
   overlay, which stretches the invisible input across a styled button and has its
   own rule for it. React renders plain file inputs with no such wrapper, so the
   rule made every upload control in the SPA render as *blank space* — the input
   present, 550x22, focusable and clickable, but completely invisible.

   That is what "there is no place to upload the file" was, on every screen that
   attaches a file: assignment hand-in, PoE evidence, briefs, slide images. It was
   previously misread as a Bootstrap `.form-control` sizing problem; removing that
   class could never have fixed it, because neither `display` nor `width` overrides
   `opacity`.

   Scoped to #root so the legacy overlay pattern keeps the transparency it needs
   outside the React tree, and re-hidden explicitly for the two patterns that do
   want an invisible input, in case either is ever mirrored into the SPA. */
#root input[type="file"] { opacity: 1; }
#root .btn-file > input[type="file"],
#root .dropify-wrapper input[type="file"] { opacity: 0; }

/* A file input that matches the other fields in the form it sits in.

   These values are MEASURED from a rendered textarea in this app, not copied
   out of bootstrap.min.css. The first attempt did the latter and came out
   wrong: Bootstrap declares .form-control as white with a 1px #ccc border at
   13px, and the legacy theme then overrides it to #f8f8f8 with a 3px #ddd
   border at 16px. Reading the base layer alone produced a field that was
   visibly foreign next to the textarea beneath it.

   The one property deliberately NOT matched is height. .form-control is fixed
   at a single line, sized for text; a native file control is taller and gets
   clipped by it, which is why the class cannot simply be applied here. Height
   stays auto and the padding gives it room. */
#root .file-control {
  display: block;
  width: 100%;
  height: auto;
  padding: 10px 20px;
  font-size: 16px;
  line-height: 1.4;
  color: #888;
  background-color: #f8f8f8;
  border: 3px solid #ddd;
  border-radius: 4px;
  box-shadow: none;
  transition: border-color ease-in-out .15s;
  cursor: pointer;
}
#root .file-control:focus {
  border-color: #ccc;
  outline: 0;
}
#root .file-control:disabled {
  background-color: #eee;
  cursor: not-allowed;
}

/* ------------------------------------------------------------------ courses

   The course catalogue as a grid of cards, which is what the old system had
   and what the client asked to have back: "This is the biggest thing on the
   LMS (COURSES). We should be able to see our courses as they were before if
   not better."

   These rules are DERIVED FROM the legacy plugin's own stylesheet,
   `public/legacy/backend/dist/css/course_addon.css` (`.coursebox`,
   `.coursebox-body`, `.stats`, `.coursebtn`), rather than by linking it.
   Linking it would have been one line, and it was the wrong line: that sheet
   also carries unscoped utilities — `.border-bottom`, `.bgwhite`, `.p0`,
   `.mb20` — and `.fa-remove { font-size: 14px !important }`, which would have
   resized a Font Awesome icon on forty screens that have nothing to do with
   courses. Exactly the class of accident as the unscoped
   `input[type=file] { opacity: 0 }` documented above, and avoided the same
   way: take the rules that are wanted, scope them to #root, and leave the
   legacy sheet to the legacy app.

   One deliberate departure from the legacy card: the title clamps to TWO
   lines, not one. The legacy rule pinned an h4 to `height: 20px`, so "National
   Certificate: Engineering Studies N4" came out as "National Certificate:
   Engi…". A course whose name is cut off is a course you cannot find by eye.

   EVERY BOX IN A ROW IS THE SAME HEIGHT, and that is the whole reason for the
   fixed sizes below. Before V47 the cover was a text band that grew with the
   code, the caption was unclamped, and one course's ten-line summary next to
   another's three words left two cards in the same row ending at wildly
   different heights — a grid that reads as broken software. The old system
   avoided it the way every catalogue does: crop every cover to one ratio and
   clamp the caption. So `.coursebox-cover` is 16:9 whatever is in it, the
   title clamps at two lines, and `.course-caption` is a fixed two lines rather
   than a minimum. Card height is then constant, with the button row pinned to
   the bottom by `.coursebox-body { flex-grow: 1 }`. */

#root .coursegrid { display: flex; flex-wrap: wrap; }
#root .coursegrid > [class*="col-"] { display: flex; }

#root .coursebox {
  position: relative;
  width: 100%;
  border: 1px solid #ccc;
  border-radius: 3px;
  margin-bottom: 20px;
  background: #fff;
  box-shadow: 0 1px 1px rgba(0, 0, 0, .05);
  display: flex;
  flex-direction: column;
}

/* The cover photograph (V47). A fixed 16:9 well whatever it holds, so a row of
   cards lines up whether the covers are portrait scans, wide banners, or
   absent. `object-fit: cover` crops rather than distorts — a stretched
   photograph is worse than a cropped one — and `object-position: center` keeps
   the middle, which is where the subject of a course thumbnail invariably is.

   `aspect-ratio` rather than the old padding-top trick: it is supported
   everywhere this application runs, and the height then holds even for the
   placeholder, which has no image in it to give it one. */
#root .coursebox-cover {
  position: relative;
  aspect-ratio: 16 / 9;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  text-align: center;
  border-bottom: 1px solid #ccc;
  border-radius: 2px 2px 0 0;
  background: #000066;
}
#root .coursebox-cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: block;
}
/* The placeholder for the courses that have no photograph — three of them have
   never had one. It states the category in words rather than encoding it in a
   hue: the band used to be one of eight hashed tints, and eight saturated
   blocks in a grid read as noise rather than as the grouping they were meant
   to convey. See COURSE_COVER_FALLBACK in courseFacts.ts. */
#root .coursebox-cover .coursebox-covernote {
  display: block;
  padding: 0 12px;
  font-size: 12px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, .85);
}

#root .coursebox-body {
  padding: 10px;
  flex-grow: 1;
  display: flex;
  flex-direction: column;
}
#root .coursebox-body h4 {
  margin: 0 0 2px;
  font-size: 16px;
  line-height: 20px;
  height: 40px;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
/* The course code, demoted from banner to caption. It was set at 18px, bold,
   letter-spaced and centred on a coloured block, which made a URL slug —
   "advanced-sales-management-n6-96" — the most prominent thing on the card.
   Monospaced and quiet here: it is an identifier staff search by, and it reads
   as one. `break-all` because these run to 40 characters with no spaces. */
#root .coursebox-body .coursebox-code {
  display: block;
  margin-bottom: 6px;
  font-family: Menlo, Consolas, monospace;
  font-size: 11px;
  color: #999;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Exactly two lines, never one and never ten. The full text is in the title
   attribute, so nothing is lost — but a caption allowed to run to its natural
   length is what made cards in one row end at different heights. */
#root .coursebox-body .course-caption {
  display: -webkit-box;
  height: 32px;
  font-size: 12px;
  line-height: 16px;
  color: #777;
  overflow: hidden;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
/* The same photograph as a banner on the preview screen. Wider and shallower
   than the card's 16:9 — the preview is one course across the full width, and
   a 16:9 crop of it would be a wall of picture before any of the detail the
   screen exists to show. */
#root .coursepreview-cover {
  aspect-ratio: 4 / 1;
  overflow: hidden;
  border-radius: 3px 3px 0 0;
  background: #000066;
}
#root .coursepreview-cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: block;
}

#root .coursebox-meta { margin: 6px 0 0; font-size: 12px; }
#root .coursebox-meta > div { margin-bottom: 2px; }
#root .coursebox-meta i { width: 14px; margin-right: 5px; color: #999; }

#root .coursebox .stats {
  border-top: 1px solid #ebeff2;
  overflow: auto;
  padding: 5px 0;
  font-size: 12px;
}
#root .coursebox .stats div {
  border-right: 1px solid #ebeff2;
  width: 33.3333%;
  float: left;
  text-align: center;
}
#root .coursebox .stats div:nth-of-type(3) { border-right: none; }
#root .coursebox .stats strong { display: block; font-size: 15px; }

#root .coursebox .coursebtn {
  background: #f4f4f4;
  border-top: 1px solid #ccc;
  padding: 8px;
  border-radius: 0 0 2px 2px;
}
#root .coursebox .coursebtn .btn { margin: 2px; }
