/* DS-04 rev 2: gradebook grading-awareness, entry-mode controls, newest-left rail (WP1-WP5) */

.gb-scalebar {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    border-radius: 10px;
    padding: 10px 14px;
    margin-bottom: 14px;
}

.gb-legend-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--text-muted);
    margin-right: 6px;
}

.gb-legend-swatches {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    font-size: 10.5px;
    color: var(--text-muted);
}

.gb-legend-item {
    display: inline-flex;
    align-items: center;
    white-space: nowrap;
}

.gb-legend-swatch {
    display: inline-block;
    width: 11px;
    height: 11px;
    border-radius: 3px;
    margin-right: 5px;
}

/* Item 5: the cell-state legend's markers ARE the grid's own cell classes
   (.b-empty/.b-missing/.b-absent), so only their legend-sized box is defined
   here - their colours stay wherever the cells define them, and the legend
   therefore cannot drift from what it explains. */
.gb-cell-states {
    gap: 12px;
}

.gb-cell-states .gb-legend-item i {
    width: 15px;
    height: 15px;
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-style: normal;
    font-size: 9px;
    font-weight: 800;
    margin-right: 5px;
    flex: none;
}

.gb-flag {
    display: inline-block;
    font-size: 8.5px;
    font-weight: 800;
    padding: 1px 6px;
    border-radius: 99px;
    letter-spacing: 0.04em;
    margin-left: 6px;
    text-transform: uppercase;
    vertical-align: middle;
}

.gb-flag-risk {
    background: rgba(239, 68, 68, 0.22);
    color: #f87171;
}

.gb-flag-drop {
    background: rgba(245, 158, 11, 0.22);
    color: #fbbf24;
}

/* One number per cell: the editable raw input, tinted by its resolved grade band.
   Kept compact so more columns fit on screen. */
.gb-cell {
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 26px;
}

/* Compound selector (.results-grade-input.gb-cell-input) so these win over the base
   .results-grade-input rule in 02_specific.css regardless of stylesheet load order. */
.results-grade-input.gb-cell-input {
    width: 28px !important;
    max-width: 34px !important;
    height: 22px !important;
    padding: 0.05rem 0.05rem !important;
    border-radius: 4px !important;
    font-weight: 700 !important;
    font-size: 0.7rem !important;
    line-height: 1 !important;
}

/* Keep the whole grade cell (input + padding) at ~28px tall. */
.results-table td.results-td-grade {
    height: 28px !important;
    padding-top: 2px !important;
    padding-bottom: 2px !important;
}

/* Tighter gridlines so the compact cells aren't padded back out. */
.results-table th,
.results-table td {
    padding: 2px 4px !important;
}

/* Much smaller date row under each column header. */
.results-table .results-date-input {
    width: 58px !important;
    font-size: 0.55rem !important;
    padding: 1px 2px !important;
}

/* Item 1: the band tints, applied to ANY element carrying a band class.
   build_final_badge() has always emitted class="final gb-band-pass" and the
   collapsed-average cells "cell cell-avg gb-band-*", but every band rule was
   scoped to .results-grade-input - so those selectors matched nothing and the
   final grade was never coloured, on any view including mobile. These generic
   rules are what make the badge follow the legend; the input-specific rules
   below still exist because a <select> needs background-color rather than the
   background shorthand (which would kill its native arrow).
   The hues are deliberately the same values as BAND_SWATCH_COLOR in
   gradebook_results_render_helpers.py, so the legend cannot drift from the
   cells it explains. */
.gb-band-fail    { background: rgba(239, 68, 68, 0.20); }
.gb-band-warn    { background: rgba(245, 158, 11, 0.18); }
.gb-band-neutral { background: rgba(148, 163, 184, 0.16); }
.gb-band-ok      { background: rgba(6, 182, 212, 0.18); }
.gb-band-pass    { background: rgba(16, 185, 129, 0.20); }

/* Band tint on the input itself (overrides the transparent base with !important). */
.results-grade-input.gb-band-fail    { background: rgba(239, 68, 68, 0.20) !important; }
.results-grade-input.gb-band-warn    { background: rgba(245, 158, 11, 0.18) !important; }
.results-grade-input.gb-band-neutral { background: rgba(148, 163, 184, 0.16) !important; }
.results-grade-input.gb-band-ok      { background: rgba(6, 182, 212, 0.18) !important; }
.results-grade-input.gb-band-pass    { background: rgba(16, 185, 129, 0.20) !important; }
/* A non-empty value that could not be resolved against the active scale (e.g.
   "9" typed into an Austrian 1-5 direct-grade column) - distinct from every
   real band so a teacher notices something needs fixing, rather than the cell
   silently reading as ungraded (D-08). */
.results-grade-input.gb-band-invalid {
    background: repeating-linear-gradient(45deg, rgba(239, 68, 68, .22), rgba(239, 68, 68, .22) 4px, transparent 4px, transparent 8px) !important;
    border: 1px solid rgba(239, 68, 68, .55) !important;
}

/* For dropdown selects, the shorthand 'background' kills the native SVG arrow. 
   We must restore the arrow and provide a subtle visible background/border for empty states. */
select.results-grade-input.gb-cell-input {
    background-color: var(--glass-bg) !important;
    background-image: var(--bs-form-select-bg-icon) !important;
    background-position: right 2px center !important;
    background-size: 10px 10px !important;
    border: 1px solid var(--glass-border) !important;
    /* Symmetric padding so the centered value isn't nudged left by the arrow's extra
       right padding. */
    padding-right: 4px !important;
    padding-left: 4px !important;
    text-align: center !important;
    text-align-last: center !important;
}

/* Ensure the band tints on dropdowns only change the background-color so the arrow isn't destroyed. */
select.results-grade-input.gb-band-fail    { background-color: rgba(239, 68, 68, 0.20) !important; }
select.results-grade-input.gb-band-warn    { background-color: rgba(245, 158, 11, 0.18) !important; }
select.results-grade-input.gb-band-neutral { background-color: rgba(148, 163, 184, 0.16) !important; }
select.results-grade-input.gb-band-ok      { background-color: rgba(6, 182, 212, 0.18) !important; }
select.results-grade-input.gb-band-pass    { background-color: rgba(16, 185, 129, 0.20) !important; }
select.results-grade-input.gb-band-invalid { background-color: rgba(239, 68, 68, .22) !important; border: 1px solid rgba(239, 68, 68, .55) !important; }

/* "Newest column" cue. A tint, not an inset edge bar: the newest column is
   always the leftmost of its part, so any left bar stacks onto the previous
   part's vertical divider, and a top bar stacks onto the row divider above the
   header cell - either way reading as a doubled black/cyan rule. A background
   tint marks the column without touching a single separator. */
.gb-newest {
    background: var(--gb-newest-bg, rgba(6, 182, 212, 0.14)) !important;
}

/* Session (participation) cells: grade input + optional note icon on the first row,
   an "Absent" line underneath. The td's 28px height acts as a minimum, so absent
   rows simply grow taller without breaking the compact grid. */
.gb-session-cell {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

.gb-cell-main {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
}

.gb-note-icon {
    color: var(--accent-secondary, #06b6d4);
    font-size: 0.65rem;
    line-height: 1;
    cursor: help;
}

.gb-absent-row {
    color: #ef4444;
    font-size: 0.6rem;
    font-weight: 600;
    line-height: 1.2;
    margin-top: 2px;
    text-align: center;
}

.gb-group-controls {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-top: 4px;
}

/* Setting-aware entry-mode label (configured in Edit Structure, read-only in the grid). */
.gb-entry-label {
    font-size: 9.5px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 700;
    color: var(--text-muted);
    padding: 1px 7px;
    border-radius: 99px;
    border: 1px solid var(--glass-border);
    background: var(--glass-bg);
}

.gb-col-controls {
    display: inline-flex;
    gap: 3px;
}

.gb-col-btn {
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    border-radius: 5px;
    font-size: 10px;
    color: var(--text-muted);
    border: 1px solid var(--glass-border);
    background: rgba(255, 255, 255, 0.04);
}

.gb-col-btn-add:hover {
    color: var(--accent-secondary);
    border-color: var(--accent-secondary);
}

.gb-col-btn-del:hover {
    color: #ef4444;
    border-color: #ef4444;
}

.gb-lock-badge {
    font-size: 9px;
    color: #10b981;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 700;
}

/* Responsive (WP6): matches the app's existing 991.98px / 767.98px breakpoints */
@media (max-width: 991.98px) {
    .gb-scalebar {
        padding: 8px 10px;
        gap: 8px;
    }

    .gb-legend-swatches {
        width: 100%;
    }
}

@media (max-width: 767.98px) {
    .gb-group-controls {
        gap: 8px;
    }

    .gb-col-btn {
        width: 26px;
        height: 26px;
    }
}

/* ==========================================================================
   DS-04 rev 3 (WP4): the grid's sticky-table skeleton, moved here from
   02_specific.css - the grid gained rails and 3-level bands, so its rules
   belong together in one file instead of split across two.
   ========================================================================== */

.results-table {
    border-collapse: separate;
    border-spacing: 0;
    font-size: 0.85rem;
    color: var(--text-main);
    background: transparent;
    width: max-content;
    min-width: 100%;
}

/* Item 6: three separator weights - hairline between columns, MEDIUM between
   parts, THICK between main areas.

   Only right+bottom are drawn, never left/top. The table is
   border-collapse: separate, so borders do NOT merge: with a border on all
   four sides every boundary was drawn twice (the left neighbour's right edge
   against the right neighbour's left edge). A part boundary therefore rendered
   as 1+1px and a main boundary as 2+1px - stacking into hard, near-black rules
   that were nearly indistinguishable from each other. Drawing each boundary
   exactly once is what makes the three weights read as three weights; it is
   also what the mock does. */
.results-table th, .results-table td {
    border: 0;
    border-right: 1px solid var(--gb-gridline, rgba(255, 255, 255, 0.06));
    border-bottom: 1px solid var(--gb-gridline, rgba(255, 255, 255, 0.06));
    padding: 4px 8px;
    vertical-align: middle;
    text-align: center;
    width: 1px;
    white-space: nowrap;
    height: 28px;
}

/* Last column of a PART - medium: groups its columns without shouting. */
.results-table th.gb-part-end,
.results-table td.gb-part-end {
    border-right: 2px solid var(--gb-part-divider, rgba(255, 255, 255, 0.16)) !important;
}

/* Last column of a main AREA - thick: the strongest line in the grid body. */
.results-table th.gb-band-end,
.results-table td.gb-band-end {
    border-right: 3px solid var(--gb-band-divider, rgba(255, 255, 255, 0.28)) !important;
}

/* Frozen rails (No. / Student / Trend / Final): ported from
   mock_gradebook_v3.html's .rail system after the previous per-side class
   split (.results-th-fixed on the <th>, .results-td-fixed on the <td>, no
   shared "left" between them) left three of the four rails not actually
   frozen - position:sticky with left:auto does not stick, so the header and
   body versions of a rail scrolled past each other independently.

   The fix is the mock's whole trick: exactly ONE class per rail, applied to
   BOTH the <th> and the <td>, with a "left" that is the running sum of the
   PINNED (min-width == max-width) widths of the rails before it. A rail
   needs a real pin, not a min/max range, or that running sum stops being
   correct the moment content forces one side wider than the other. */
.gb-rail {
    position: sticky !important;
    background: var(--bg-color) !important;
    z-index: 25 !important;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
}

.gb-rail-no {
    left: 0 !important;
    min-width: 44px !important;
    max-width: 44px !important;
    border-right: 1px solid var(--glass-border) !important;
}

/* Sized to fit a long first name plus the SLIPPING chip on one line (~118px);
   going much narrower clips it, since the cell is nowrap + ellipsis. */
.gb-rail-name {
    left: 44px !important;
    min-width: 140px !important;
    max-width: 140px !important;
    text-align: left !important;
}

/* The true frozen/scrolling boundary - the shadow that used to sit on every
   left rail (redundant, since they're opaque and adjacent) now lives only
   here, where it's actually meaningful. */
.gb-rail-trend {
    left: 184px !important;
    min-width: 90px !important;
    max-width: 90px !important;
    text-align: center;
    border-right: 2px solid var(--glass-border) !important;
    box-shadow: 2px 0 5px rgba(0, 0, 0, 0.3);
}

/* Frozen right rail (D "Frozen right rail: period final grade"). */
.gb-rail-final {
    right: 0 !important;
    min-width: 68px !important;
    max-width: 68px !important;
    text-align: center;
    border-left: 2px solid var(--glass-border) !important;
    box-shadow: -2px 0 5px rgba(0, 0, 0, 0.3);
}

/* Explicit per-row heights, and "top" set to their running sum, so the three
   sticky header rows tile exactly with no gap or overlap between them - a
   table cell's "height" is a floor, not a cap, so these are sized generously
   for what each row actually renders (row 1: a collapse icon + label on one
   line; row 3: a rename label stacked over its date line) rather than
   assuming a uniform height for all three, which is what let a taller row
   drift out of sync with the next row's fixed "top" before. */
.results-table thead tr:nth-child(1) th {
    position: sticky;
    top: 0;
    height: 30px;
    z-index: 20;
    background: var(--bg-color);
    overflow: hidden;
}

.results-table thead tr:nth-child(2) th {
    position: sticky;
    top: 30px;
    height: 30px;
    z-index: 20;
    background: var(--bg-color);
    overflow: hidden;
}

.results-table thead tr:nth-child(3) th {
    position: sticky;
    top: 60px;
    height: 38px;
    z-index: 20;
    background: var(--bg-color);
    overflow: hidden;
}

/* Intersection of a sticky row and a sticky rail column - the strict ladder
   is: ordinary body cells (unlayered) < sticky header rows (20) < body rail
   cells (25) < this intersection (35), so a rail always stays visible over
   whatever it's passing, and the header always stays on top of a rail
   scrolling underneath it vertically. */
.results-table thead th.gb-rail {
    z-index: 35 !important;
}

.results-th-group {
    background: var(--group-bg, rgba(139, 92, 246, 0.08)) !important;
    color: var(--text-main) !important;
    text-transform: uppercase;
    font-size: 0.7rem;
    letter-spacing: 0.08em;
    font-weight: 600;
    border-bottom: 1px solid var(--glass-border) !important;
}

.results-th-sub {
    background: rgba(255, 255, 255, 0.02) !important;
    font-size: 0.75rem;
    color: var(--text-muted) !important;
    font-weight: 500;
}

.th-area-collapsed, .th-sub-collapsed {
    background: rgba(255, 255, 255, 0.03) !important;
    font-size: 0.75rem;
    cursor: pointer;
}

.results-row:hover {
    background: rgba(255, 255, 255, 0.03) !important;
}

.results-td-collapsed {
    background: rgba(255, 255, 255, 0.02) !important;
    border-right: 1px solid var(--glass-border) !important;
}

.results-tabs {
    border-bottom: 1px solid var(--glass-border) !important;
}

.results-tabs .nav-link {
    background: transparent !important;
    border: none !important;
    color: var(--text-muted) !important;
    font-weight: 600;
    font-size: 0.85rem;
    padding: 0.5rem 1.25rem;
    transition: all 0.2s;
}

.results-tabs .nav-link:hover {
    color: var(--text-main) !important;
}

.results-tabs .nav-link.active {
    color: var(--accent-primary) !important;
    position: relative;
}

.results-tabs .nav-link.active::after {
    content: '';
    position: absolute;
    bottom: -1px;
    left: 0;
    right: 0;
    height: 2px;
    background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary));
}

.results-scroll-container {
    max-height: calc(100vh - 220px);
    max-width: 100%;
    overflow: auto;
    border-radius: 12px;
    border: 1px solid var(--glass-border);
    background: var(--table-container-bg);
    position: relative;
    box-shadow: 0 10px 30px -10px rgba(0, 0, 0, 0.2);
}

.results-scroll-container::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

.results-scroll-container::-webkit-scrollbar-track {
    background: rgba(0, 0, 0, 0.3);
}

.results-scroll-container::-webkit-scrollbar-thumb {
    background: var(--glass-border);
    border-radius: 5px;
    border: 2px solid transparent;
    background-clip: content-box;
}

.results-scroll-container::-webkit-scrollbar-thumb:hover {
    background: var(--accent-primary);
}

/* Item 10: the column date is editable again, but a native date input is far
   wider than the 40px leaf column, so the formatted short date (.dt) carries
   the layout and the actual <input type=date> sits behind it, invisible and
   non-interactive - see the .dt-edit comment below for how it still opens. */
/* Item 7: the collapse caret on a part row in the structure editor. */
.struct-collapse {
    cursor: pointer;
    color: var(--text-muted);
    flex: none;
}

.struct-collapse:hover {
    color: var(--text-main);
}

.dt-wrap {
    position: relative;
    display: block;
    cursor: pointer;
}

/* The overlay <input> is not the click target - its precise hit box was
   unreliable (too thin, easy to miss, sometimes shadowed by a neighbouring
   sticky cell) so clicks kept landing "beside" the visible date instead of
   opening the picker. pointer-events:none takes it out of hit-testing
   entirely: every click in .dt-wrap lands on the always-clickable .dt span,
   and 08_gb_leaf_date.js explicitly opens this input's picker from there. */
.dt-wrap .dt-edit {
    position: absolute;
    inset: 0;
    width: 100% !important;
    min-width: 0 !important;
    padding: 0 !important;
    margin: 0 !important;
    border: none !important;
    background: transparent !important;
    color: transparent !important;
    opacity: 0.01;
    pointer-events: none;
}

.dt-wrap:hover .dt {
    text-decoration: underline dotted;
}

.results-date-input {
    width: 80px !important;
    background: transparent !important;
    border: 1px solid transparent !important;
    color: var(--text-main) !important;
    font-size: 0.65rem !important;
    text-align: center;
    border-radius: 4px;
    padding: 1px 2px !important;
    margin: 0 auto;
    display: block;
    transition: all 0.2s;
}

.results-date-input:hover {
    border-color: var(--glass-border) !important;
    background: rgba(255, 255, 255, 0.02) !important;
}

.results-date-input::-webkit-calendar-picker-indicator {
    filter: invert(0.8);
    opacity: 0.6;
    cursor: pointer;
    margin-left: -15px;
}

.results-grade-input {
    width: 100% !important;
    background: transparent !important;
    border: none !important;
    color: var(--text-main) !important;
    text-align: center !important;
    font-weight: 700 !important;
    font-size: 0.8rem !important;
    padding: 0.15rem 0.3rem !important;
    border-radius: 4px !important;
    transition: all 0.2s ease-in-out !important;
}

.results-grade-input:focus {
    border-color: var(--accent-primary) !important;
    box-shadow: 0 0 6px rgba(16, 185, 129, 0.4) !important;
    background: rgba(255, 255, 255, 0.05) !important;
}

/* Divider styling for calculated summary fields placed next to Student Name.
   Light mode override is in theme-light.css. */
.results-summary-divider {
    border-right: 2px solid rgba(255, 255, 255, 0.15) !important;
}

/* ==========================================================================
   DS-04 rev 3 (WP4): 3-level header bands, empty/collapsed states, recent
   window, quick add, inline rename, bulk menu trigger, and cell states.
   ========================================================================== */

.empty-band {
    color: var(--text-muted);
    font-weight: 600;
    font-size: 0.7rem;
    padding: 0 10px;
    text-align: center;
    background: repeating-linear-gradient(45deg, rgba(255, 255, 255, .02), rgba(255, 255, 255, .02) 6px, transparent 6px, transparent 12px) !important;
    white-space: normal !important;
}

.sub-add {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: rgba(6, 182, 212, .18);
    border: 1px solid rgba(6, 182, 212, .45);
    color: #67e8f9;
    font-size: 11px;
    border-radius: 5px;
    padding: 0 7px;
    margin-left: 6px;
    cursor: pointer;
    font-weight: 800;
}

.sub-add:hover {
    background: rgba(6, 182, 212, .34);
    color: #fff;
}

.sub-more {
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid var(--glass-border);
    color: var(--text-muted);
    font-size: 9.5px;
    border-radius: 5px;
    padding: 1px 6px;
    margin-left: 6px;
    cursor: pointer;
    font-weight: 600;
}

.sub-more:hover {
    color: var(--text-main);
}

.daily-tag {
    font-size: 8.5px;
    color: var(--text-muted);
    font-weight: 600;
    letter-spacing: .04em;
}

.th-leaf {
    font-size: 10.5px;
    font-weight: 600;
    min-width: 40px;
    max-width: 40px;
    position: relative;
}

.th-leaf .lab {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 36px;
    margin: 0 auto;
    cursor: text;
}

.th-leaf .dt {
    display: block;
    color: var(--text-muted);
    font-size: 9px;
    font-weight: 500;
    margin-top: 1px;
}

.th-leaf .bulk {
    position: absolute;
    top: 1px;
    right: 1px;
    background: transparent;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    font-size: 10px;
    line-height: 1;
    padding: 1px 2px;
    border-radius: 3px;
}

.th-leaf .bulk:hover {
    background: rgba(255, 255, 255, 0.15);
    color: var(--text-main);
}

/* An <input> carries an intrinsic preferred width (~170px) that a table cell
   grows to fit, so width:100% alone let a freshly added column blow its 40px
   band wide open. min-width:0 + border-box is what actually lets it shrink. */
/* Item 2: the rename box is taken out of flow (the mock does the same), so it
   can be a comfortable 150px while its FONT still matches .th-leaf's 10.5px.
   Constrained to the 40px column it previously had to shrink to 8px - visibly
   smaller than every other heading - and needed an html_size hack to stop the
   input's intrinsic width blowing the column open. Out of flow, the column's
   width is untouched no matter how wide the box is. */
.lab-edit {
    position: absolute !important;
    left: 50% !important;
    top: 1px !important;
    transform: translateX(-50%);
    z-index: 50 !important;
    width: 150px !important;
    min-width: 0 !important;
    max-width: none !important;
    box-sizing: border-box !important;
    background: var(--bg-color) !important;
    border: 1px solid var(--accent-secondary) !important;
    color: var(--text-main) !important;
    border-radius: 6px !important;
    padding: 3px 6px !important;
    font-size: 10.5px !important;
    font-weight: 700 !important;
    text-align: center !important;
    box-shadow: 0 8px 22px rgba(0, 0, 0, .45);
}

/* The renaming header cell must sit above its neighbours, or the overhanging
   box is clipped by the next sticky column. */
.results-table thead th.th-renaming {
    z-index: 45 !important;
}

/* Body cells (D9 four states + the D4/WP5 note/editor opener). */
/* The pen sits in reserved space at the top-right rather than floating over the
   grade box (item 4): padding-right keeps the input clear of it at every width,
   which is what the 768-992px overlap came down to. */
.gb-cell-wrap {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    padding-right: 9px;
}

.cell {
    width: 32px;
    height: 22px;
    margin: 0 auto;
    border-radius: 5px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 800;
    font-size: 11px;
}

.cell-avg {
    width: 40px;
    font-size: 11px;
}

.final {
    width: 40px;
    height: 24px;
    margin: 0 auto;
    border-radius: 6px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 800;
    font-size: 12px;
}

/* Item 5/17: the "not graded yet" state. The mock has always had this; the app
   never did, which is why an empty cell and a graded one looked identical on
   mobile (item 17) and why the cell-state legend had nothing to reuse. */
.b-empty {
    background: transparent;
    border: 1px dashed var(--glass-border);
    color: transparent;
}

.b-missing {
    background: rgba(248, 113, 113, .22);
    color: #fca5a5;
    text-decoration: line-through;
}

.b-absent {
    background: rgba(148, 163, 184, .16);
    color: #cbd5e1;
}

/* Items 3 + 4: the note/editor opener.
   3 - a pen, not an abstract dot, so it reads as "write something here"; both
       states are driven by theme variables, because the old "no note" state was
       a hardcoded 25%-white ring that was invisible on the light theme.
   4 - anchored to .gb-cell-wrap's own top-right corner and given real space by
       the wrap's padding, instead of being absolutely positioned over whatever
       the cell contained. Between 768 and 992px the input used to overflow the
       narrowed column and the icon landed on top of the grade box. */
.note-pen {
    position: absolute;
    top: 0;
    right: 0;
    line-height: 1;
    font-size: 8px;
    cursor: pointer;
    color: var(--accent-secondary, #06b6d4);
    opacity: 0.95;
}

.note-pen-empty {
    color: var(--text-muted);
    opacity: 0.4;
}

.note-pen:hover {
    opacity: 1;
    color: var(--accent-primary);
}

.stu-last {
    font-weight: 700;
    font-size: 12.5px;
    line-height: 1.15;
}

.stu-first {
    font-weight: 500;
    font-size: 11px;
    color: var(--text-muted);
    line-height: 1.15;
}

/* First name and the at-risk/slipping chip share one line - the chip used to
   wrap onto a third line and make flagged rows taller than the rest. */
.stu-first-row {
    line-height: 1.15;
}

/* The 991.98px .th-leaf narrowing rule that used to live here (40px -> 34px)
   is deliberately removed (items 4/12): the table is already
   width:max-content inside an always-overflow:auto container, so narrowing
   columns only delayed when horizontal scrolling visually kicked in, while
   squeezing the grade input (still 28px/34px) against the note indicator
   until the two overlapped. Columns now stay a comfortable 40px at every
   width down to the 767.98px mobile switchover, and the grid scrolls sooner
   instead of cramming - the correct trade-off, since scrolling was always
   available (overflow:auto is unconditional) but narrowing wasn't.
   ========================================================================== */

/* ==========================================================================
   DS-04 rev 3 (WP5): the universal grade cell editor (D4/D6) - shared by the
   grid's note-dot opener; WP7's mobile bottom sheet reuses the same classes.
   ========================================================================== */

.ce-meta {
    font-size: 12px;
    color: var(--text-muted);
}

.scale-row, .flag-row {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

.scale-btn {
    width: 44px;
    height: 44px;
    border-radius: 10px;
    border: 1px solid var(--glass-border);
    background: rgba(255, 255, 255, .05);
    color: var(--text-main);
    font-weight: 800;
    font-size: 16px;
    cursor: pointer;
}

.scale-btn.on {
    outline: 2px solid var(--accent-secondary, #06b6d4);
}

.flag-btn {
    border: 1px solid var(--glass-border);
    background: rgba(255, 255, 255, .05);
    color: var(--text-main);
    border-radius: 9px;
    padding: 8px 14px;
    font-size: 12.5px;
    cursor: pointer;
    font-weight: 600;
}

.flag-btn.on {
    outline: 2px solid var(--accent-secondary, #06b6d4);
}

/* ==========================================================================
   DS-04 rev 3 (WP7, D5, section 4.7): the grid is desktop/tablet only - a
   student-first card view takes over on phones. Pure CSS switch (no viewport
   JS), matching the calendar's own responsive pattern: both views render,
   only one is shown, toggled at the app's existing 767.98px breakpoint.
   ========================================================================== */

.gb-mobile-only {
    display: none;
}

@media (max-width: 767.98px) {
    .gb-desktop-only {
        display: none;
    }
    .gb-mobile-only {
        display: block;
    }
}

.gb-mobile-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 10px;
}

.ph-stu {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 11px 12px;
    border-radius: 11px;
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    cursor: pointer;
}

.ph-stu .no {
    font-size: 11px;
    color: var(--text-muted);
    font-weight: 700;
    min-width: 20px;
}

.ph-stu .nm {
    font-weight: 700;
    font-size: 13.5px;
    display: block;
}

.ph-stu .sub {
    font-size: 11px;
    color: var(--text-muted);
    display: block;
}

.ph-fin {
    margin-left: auto;
}

.gb-mobile-detail {
    padding: 10px;
}

.ph-acc {
    border: 1px solid var(--glass-border);
    border-radius: 11px;
    overflow: hidden;
}

.ph-acc-head {
    padding: 11px 12px;
    cursor: pointer;
    background: rgba(255, 255, 255, .03);
}

.ph-leaf {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 12px;
    border-top: 1px solid rgba(255, 255, 255, .06);
}

.ph-leaf .nm {
    font-size: 12.5px;
}

/* Item 17: a mark on mobile must read as something tappable and changeable.
   It used to be a bare .cell with no band class and no default border, so a
   graded and an ungraded row looked identical. It now always carries a state
   (band / X / A / dashed empty), and is sized to a real touch target - the
   desktop's 32x22px is well under the ~44px this stylesheet already grants
   other touch controls at this breakpoint. */
.ph-leaf .cell {
    margin: 0 0 0 auto;
    cursor: pointer;
    min-width: 46px;
    height: 34px;
    border-radius: 8px;
    font-size: 13px;
}

/* An empty cell still needs to look like an input, not blank space. */
.ph-leaf .cell.b-empty {
    border: 1px dashed var(--glass-border);
}

/* Back + the gradebook switcher share their own row above the title and the
   grading-scale card. The switcher is capped so it stays beside Back rather
   than stretching across the page. */
.gb-nav-row .gb-quick-nav {
    max-width: 320px;
    flex: 0 1 240px;
}

@media (max-width: 767.98px) {
    .gb-nav-row .gb-quick-nav {
        flex: 1 1 100%;
        max-width: none;
        margin-left: 0 !important;
    }
}

/* Item 19: the toolbar had no flex-wrap and sat outside both the desktop and
   mobile wrappers, so its ~600px of non-wrapping content (a 160px-minimum sort
   dropdown plus two full-text buttons) squeezed the buttons out of shape on a
   phone. It wraps now, and below the mobile breakpoint the two long buttons
   drop to their icons - their title attributes carry the same wording. */
@media (max-width: 767.98px) {
    .gb-toolbar-actions {
        flex-wrap: wrap;
        gap: 6px;
    }

    .gb-btn-label {
        display: none;
    }

    .gb-toolbar-actions .fa-copy,
    .gb-toolbar-actions .fa-gear {
        margin-right: 0 !important;
    }

    .gb-sort-select {
        min-width: 140px !important;
        flex: 1 1 140px;
    }
}

/* ==========================================================================
   Gradebook overview: per-class cards with drag-to-reorder (section 4.6).
   Replaces the old flat class x subject x group table, which had no per-class
   row to drag. Ported from mock_gradebook_v3.html.
   ========================================================================== */

.gb-class-cards {
    display: flex;
    flex-direction: column;
    gap: 10px;
    max-width: 760px;
}

.gb-class-card {
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    border-radius: 10px;
    padding: 10px 12px;
    cursor: grab;
}

.gb-class-card.dragging {
    opacity: 0.45;
    cursor: grabbing;
}

/* Students with no class have no row in `classes`, so there is nothing to
   persist an order against - that card is not draggable. */
.gb-class-card-static {
    cursor: default;
    opacity: 0.85;
}

.gb-class-head {
    display: flex;
    align-items: center;
    gap: 10px;
}

.gb-handle {
    color: var(--text-muted);
    cursor: grab;
    letter-spacing: -2px;
    font-size: 15px;
    user-select: none;
}

.gb-class-dot {
    width: 12px;
    height: 12px;
    border-radius: 3px;
    flex: none;
}

.gb-class-title {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.gb-class-name {
    font-weight: 700;
    font-size: 14px;
    color: var(--text-main);
}

.gb-class-meta {
    font-size: 11px;
    color: var(--text-muted);
}

.gb-swatches {
    display: flex;
    gap: 5px;
    margin-left: auto;
}

.gb-sw {
    width: 14px;
    height: 14px;
    border-radius: 4px;
    cursor: pointer;
    border: 2px solid transparent;
}

.gb-sw.on {
    border-color: var(--text-main);
}

.gb-class-combos {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-top: 8px;
}

.gb-combo-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 6px;
    border-radius: 6px;
}

.gb-combo-row:hover {
    background: rgba(255, 255, 255, 0.05);
}

.gb-combo-link {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 1;
    min-width: 0;
    text-decoration: none;
    color: inherit;
}

.gb-combo-subject {
    font-size: 12px;
    color: var(--text-main);
}

.gb-combo-group {
    font-size: 11px;
    color: var(--text-muted);
}

.gb-combo-count {
    margin-left: auto;
}

.gb-combo-actions {
    display: flex;
    flex: none;
}

@media (max-width: 767.98px) {
    .gb-swatches {
        gap: 8px;
    }

    .gb-sw {
        width: 18px;
        height: 18px;
    }
}
