/* ==========================================================================
   cbtt-ui — the suite's Bootstrap replacement (BR-2).
   --------------------------------------------------------------------------
   Buttons, grid, cards, alerts, badges, form basics, a native <dialog> modal
   and a <details> collapse. Registered by core/design/ui_assets.php.

   Design standard (see markdowns/plugin/DESIGN-SYSTEM.md, Win Timetable
   Solver repo):
     - Flat surfaces: 1px var(--cbtt-border) + var(--cbtt-radius-md). No
       box-shadow anywhere except the modal (elevation is real there — it
       floats above a backdrop) and the :focus-visible ring (a token-driven
       accessibility affordance, not decoration).
     - Every color goes through var(--cbtt-*, literal-fallback) — never a
       bare hex — so the 'theme' preset (no palette printed) still renders
       sane colors, and every other preset repaints these components for
       free.
     - :focus-visible = 2px solid var(--cbtt-teal) outline + var(--cbtt-focus-
       ring) box-shadow, consistently across every interactive element.
     - No gradients.
     - prefers-reduced-motion strips all transitions/animations in one place
       at the bottom of this file.

   Depends on core/assets/css/tokens.css (spacing/radius/shadow-lg tokens)
   and the palette inline block from design_tokens.php (--cbtt-primary,
   --cbtt-surface, --cbtt-border, status colors, --cbtt-focus-ring, ...) —
   both load first via the 'cbtt-suite-tokens' dependency.
   ========================================================================== */

/* ---- Buttons -------------------------------------------------------------
   .cbtt-btn + one color modifier (--primary/--secondary/--ghost/--danger/
   --danger-solid) + optional size modifier (--sm/--lg) + optional --block.
   --danger-solid is a filled destructive style — reserve it for the final
   confirm action inside a modal, never for a plain list-row delete link
   (use --danger, the outline style, there). */

.cbtt-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-height: 40px;
    padding: 8px 16px;
    border: 1px solid transparent;
    border-radius: var(--cbtt-radius-md, 11px);
    font-family: inherit;
    font-size: var(--cbtt-fs-sm, 0.875rem);
    font-weight: 600;
    line-height: 1.3;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    transition: color 150ms ease, background-color 150ms ease, border-color 150ms ease;
}

.cbtt-btn:focus-visible {
    outline: 2px solid var(--cbtt-teal, #0193aa);
    outline-offset: 2px;
    box-shadow: var(--cbtt-focus-ring, 0 0 0 3px rgba(1, 147, 170, 0.15));
}

a.cbtt-btn:visited {
    /* Theme-Link-Falle: WP admin/theme stylesheets style a:visited on their
       own (often a desaturated purple) — without this it wins over the
       modifier color below on repeat visits to the same URL. */
    color: inherit;
}

.cbtt-btn--primary,
a.cbtt-btn--primary:visited {
    background: var(--cbtt-primary, #003f86);
    color: var(--cbtt-on-primary, #ffffff);
}
.cbtt-btn--primary:hover {
    background: var(--cbtt-primary-hover, #002b57);
}

.cbtt-btn--secondary,
a.cbtt-btn--secondary:visited {
    background: var(--cbtt-surface, #ffffff);
    border-color: var(--cbtt-border, #e6e1d7);
    color: var(--cbtt-text, #1f2733);
}
.cbtt-btn--secondary:hover {
    background: var(--cbtt-surface-alt, #f7f3ec);
}

.cbtt-btn--ghost,
a.cbtt-btn--ghost:visited {
    background: transparent;
    color: var(--cbtt-accent-ink, #086b7e);
}
.cbtt-btn--ghost:hover {
    background: var(--cbtt-surface-alt, #f7f3ec);
}

.cbtt-btn--danger,
a.cbtt-btn--danger:visited {
    background: transparent;
    border-color: var(--cbtt-error-ink, #992e26);
    color: var(--cbtt-error-ink, #992e26);
}
.cbtt-btn--danger:hover {
    background: var(--cbtt-error-tint, #fbe3e0);
}

/* Filled destructive — final confirmation only, see banner comment above. */
.cbtt-btn--danger-solid,
a.cbtt-btn--danger-solid:visited {
    background: var(--cbtt-error, #c4443a);
    border-color: var(--cbtt-error, #c4443a);
    color: var(--cbtt-on-primary, #ffffff);
}
.cbtt-btn--danger-solid:hover {
    background: var(--cbtt-error-ink, #992e26);
    border-color: var(--cbtt-error-ink, #992e26);
}

/* Theme-Link-Falle, zweiter Teil: an <a> button inside page content. Themes
   colour prose links with one class plus the element — CBTT-Turbo's
   `.cbtt-content a` (0,1,1), elsewhere `.entry-content a` — and that outranks
   every single-class modifier above. The button then took the link colour and
   an underline: on the student calendar "My availability" was link colour on
   a fill of the same colour, i.e. no visible label (measured 2026-09-27; under
   Orange both were #0073aa, under Solver Blue teal on navy). Class + element +
   pseudo-class (0,2,1) wins without relying on source order. */
a.cbtt-btn:is(:link, :visited, :hover, :focus, :active) {
    text-decoration: none;
}
a.cbtt-btn--primary:is(:link, :visited, :hover, :focus, :active),
a.cbtt-btn--danger-solid:is(:link, :visited, :hover, :focus, :active) {
    color: var(--cbtt-on-primary, #ffffff);
}
a.cbtt-btn--secondary:is(:link, :visited, :hover, :focus, :active) {
    color: var(--cbtt-text, #1f2733);
}
a.cbtt-btn--ghost:is(:link, :visited, :hover, :focus, :active) {
    color: var(--cbtt-accent-ink, #086b7e);
}
a.cbtt-btn--danger:is(:link, :visited, :hover, :focus, :active) {
    color: var(--cbtt-error-ink, #992e26);
}

.cbtt-btn--sm {
    min-height: 32px;
    padding: 4px 10px;
    gap: 4px;
    font-size: 12.5px;
}

.cbtt-btn--lg {
    min-height: 46px;
    padding: 10px 20px;
}

.cbtt-btn--block {
    display: flex;
    width: 100%;
}

/* Disabled state last on purpose — same selector specificity as the color
   modifiers above, so source order decides the winner. Never opacity-only:
   an opacity-dimmed primary button still reads as "click me" on a busy
   admin screen. */
.cbtt-btn:disabled,
.cbtt-btn[aria-disabled="true"] {
    background: var(--cbtt-border-soft, #eceae4);
    border-color: var(--cbtt-border-soft, #eceae4);
    color: var(--cbtt-text-muted, #7c8494);
    cursor: not-allowed;
}
.cbtt-btn:disabled:hover,
.cbtt-btn[aria-disabled="true"]:hover {
    background: var(--cbtt-border-soft, #eceae4);
    border-color: var(--cbtt-border-soft, #eceae4);
}

/* ---- Grid ------------------------------------------------------------- */

.cbtt-ui-row {
    --cbtt-ui-gap: var(--cbtt-space-4, 16px);
    display: flex;
    flex-wrap: wrap;
    gap: var(--cbtt-ui-gap);
}
.cbtt-ui-row--tight {
    --cbtt-ui-gap: 8px;
}
.cbtt-ui-row--center {
    justify-content: center;
}
.cbtt-ui-row--end {
    justify-content: flex-end;
}
.cbtt-ui-row--between {
    justify-content: space-between;
}
.cbtt-ui-row--middle {
    align-items: center;
}

.cbtt-ui-col {
    flex: 1 1 0;
    min-width: 0;
}
.cbtt-ui-col--auto {
    flex: 0 0 auto;
}
/* The fractions are net of the row gap, so that the columns of one row add
   up to the row: with a plain 66.66% + 33.33% the gap pushed the second
   column onto the next line, and the "Street | House number" pair on the
   contract form stacked on every width (measured 2026-09-23). Rule: a column
   of n/12 gives up (1 - n/12) of one gap. */
.cbtt-ui-col--3 {
    flex: 0 0 calc(25% - var(--cbtt-ui-gap, 16px) * 0.75);
    max-width: calc(25% - var(--cbtt-ui-gap, 16px) * 0.75);
}
.cbtt-ui-col--4 {
    flex: 0 0 calc(33.333% - var(--cbtt-ui-gap, 16px) * 0.6667);
    max-width: calc(33.333% - var(--cbtt-ui-gap, 16px) * 0.6667);
}
.cbtt-ui-col--6 {
    flex: 0 0 calc(50% - var(--cbtt-ui-gap, 16px) * 0.5);
    max-width: calc(50% - var(--cbtt-ui-gap, 16px) * 0.5);
}
.cbtt-ui-col--8 {
    flex: 0 0 calc(66.667% - var(--cbtt-ui-gap, 16px) * 0.3333);
    max-width: calc(66.667% - var(--cbtt-ui-gap, 16px) * 0.3333);
}

@media (max-width: 782px) {
    .cbtt-ui-col--3,
    .cbtt-ui-col--4,
    .cbtt-ui-col--6,
    .cbtt-ui-col--8 {
        flex-basis: 100%;
        max-width: 100%;
    }
}

/* ---- Card --------------------------------------------------------------
   Flat by design-standard: surface + 1px border + radius-md, no shadow. */

.cbtt-ui-card {
    background: var(--cbtt-surface, #ffffff);
    border: 1px solid var(--cbtt-border, #e6e1d7);
    border-radius: var(--cbtt-radius-md, 11px);
}
.cbtt-ui-card__header {
    padding: 16px 20px;
    border-bottom: 1px solid var(--cbtt-border-soft, #f0ebe1);
}
.cbtt-ui-card__body {
    padding: 20px;
}
.cbtt-ui-card__footer {
    padding: 12px 20px;
    border-top: 1px solid var(--cbtt-border-soft, #f0ebe1);
}
.cbtt-ui-card__title {
    margin: 0;
    font-size: 17px;
    font-weight: 600;
    color: var(--cbtt-text, #1f2733);
}

/* ---- Alert -------------------------------------------------------------
   Tint background + ink text + 3px left border, one status per modifier. */

.cbtt-ui-alert {
    padding: 12px 16px;
    border-radius: var(--cbtt-radius-sm, 8px);
    border-left: 3px solid transparent;
}
.cbtt-ui-alert--success {
    background: var(--cbtt-success-tint, #e4f3ec);
    border-left-color: var(--cbtt-success, #1f8f5f);
    color: var(--cbtt-success-ink, #166b46);
}
.cbtt-ui-alert--warning {
    background: var(--cbtt-warning-tint, #fbefd8);
    border-left-color: var(--cbtt-warning, #c67a12);
    color: var(--cbtt-warning-ink, #8a570a);
}
.cbtt-ui-alert--error {
    background: var(--cbtt-error-tint, #fbe3e0);
    border-left-color: var(--cbtt-error, #c4443a);
    color: var(--cbtt-error-ink, #992e26);
}
.cbtt-ui-alert--info {
    background: var(--cbtt-info-tint, #e6effa);
    border-left-color: var(--cbtt-info, #003f86);
    color: var(--cbtt-info-ink, #003f86);
}

/* ---- Badge -------------------------------------------------------------- */

.cbtt-ui-badge {
    display: inline-flex;
    align-items: center;
    padding: 4px 10px;
    border-radius: var(--cbtt-radius-pill, 999px);
    font-size: 12px;
    font-weight: 700;
    line-height: 1.4;
}
.cbtt-ui-badge--success {
    background: var(--cbtt-success-tint, #e4f3ec);
    color: var(--cbtt-success-ink, #166b46);
}
.cbtt-ui-badge--warning {
    background: var(--cbtt-warning-tint, #fbefd8);
    color: var(--cbtt-warning-ink, #8a570a);
}
.cbtt-ui-badge--error {
    background: var(--cbtt-error-tint, #fbe3e0);
    color: var(--cbtt-error-ink, #992e26);
}
.cbtt-ui-badge--info {
    background: var(--cbtt-info-tint, #e6effa);
    color: var(--cbtt-info-ink, #003f86);
}
.cbtt-ui-badge--neutral {
    background: var(--cbtt-surface-alt, #f7f3ec);
    color: var(--cbtt-body, #4a5262);
}

/* ---- Forms ---------------------------------------------------------------
   .cbtt-ui-input targets text/select/textarea alike via the shared class. */

.cbtt-ui-label {
    display: block;
    margin-bottom: 4px;
    font-size: 13px;
    font-weight: 600;
    color: var(--cbtt-text, #1f2733);
}

.cbtt-ui-input {
    display: block;
    width: 100%;
    max-width: min(420px, 100%);
    padding: 8px 12px;
    border: 1px solid var(--cbtt-border, #e6e1d7);
    border-radius: var(--cbtt-radius-md, 11px);
    background: var(--cbtt-surface, #ffffff);
    color: var(--cbtt-text, #1f2733);
    font-family: inherit;
    font-size: var(--cbtt-fs-sm, 0.875rem);
    transition: border-color 150ms ease, box-shadow 150ms ease;
}
.cbtt-ui-input:focus-visible {
    outline: none;
    border-color: var(--cbtt-teal, #0193aa);
    box-shadow: var(--cbtt-focus-ring, 0 0 0 3px rgba(1, 147, 170, 0.15));
}
.cbtt-ui-input--full {
    max-width: none;
}
.cbtt-ui-input--narrow {
    max-width: 120px;
}

.cbtt-ui-field {
    margin-bottom: 16px;
}

.cbtt-ui-help {
    display: block;
    max-width: var(--cbtt-measure);
    margin-top: 4px;
    font-size: 12.5px;
    color: var(--cbtt-text-muted, #7c8494);
}

.cbtt-ui-check {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: var(--cbtt-fs-sm, 0.875rem);
    color: var(--cbtt-text, #1f2733);
    cursor: pointer;
}
.cbtt-ui-check input[type="checkbox"],
.cbtt-ui-check input[type="radio"] {
    margin: 0;
}
.cbtt-ui-check input:focus-visible {
    outline: 2px solid var(--cbtt-teal, #0193aa);
    outline-offset: 2px;
    box-shadow: var(--cbtt-focus-ring, 0 0 0 3px rgba(1, 147, 170, 0.15));
}

/* ---- Weekday picker ------------------------------------------------------
   Seven checkboxes in one wrapping row, inside a <fieldset> whose <legend>
   is the field label (cbtt_sm_contract_render_weekday_picker(),
   modules/students/contracts/contract_data.php). Each day IS a
   .cbtt-ui-check, which is why this sits directly after that component —
   and after ALL of it: dropped into the middle, it split the base rule from
   its own input rules. The fieldset's own border and padding are removed:
   the grouping is carried by the legend and the proximity of the boxes,
   which is the weakest bracket that works (UX-GUIDELINES 1.1) — a framed box
   here would read as a card. */
.cbtt-ui-weekdays {
    min-width: 0; /* a <fieldset> refuses to shrink below its content otherwise */
    margin: 0;
    padding: 0;
    border: 0;
}

.cbtt-ui-weekdays > legend {
    float: none; /* some admin sheets float legends; that would detach the row */
    width: auto;
    padding: 0;
    margin-bottom: 4px;
}

.cbtt-ui-weekdays__row {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 14px;
}

.cbtt-ui-weekdays__day {
    /* Wide enough that the row breaks between days, never inside a day. */
    white-space: nowrap;
}

.cbtt-ui-weekdays__raw {
    max-width: var(--cbtt-measure);
    margin: 6px 0 0;
    font-size: 12.5px;
    color: var(--cbtt-text-muted, #7c8494);
}

/* ---- Modal ---------------------------------------------------------------
   The one place box-shadow is allowed: the dialog floats over its own
   ::backdrop, so real elevation is warranted. Native <dialog>; migration
   files render <dialog class="cbtt-ui-modal"> and drive it via CbttUi.modal
   or the data-cbtt-modal-* attributes (cbtt-ui.js). */

dialog.cbtt-ui-modal {
    width: calc(100vw - 32px);
    max-width: 520px;
    padding: 0;
    border: 0;
    border-radius: var(--cbtt-radius-lg, 16px);
    box-shadow: var(--cbtt-shadow-lg, 0 14px 40px rgba(0, 43, 87, 0.08));
    background: var(--cbtt-surface, #ffffff);
    color: var(--cbtt-text, #1f2733);
}
dialog.cbtt-ui-modal--wide {
    max-width: 720px;
}
dialog.cbtt-ui-modal::backdrop {
    background: rgba(0, 43, 87, 0.5);
}

@keyframes cbtt-ui-modal-fade-in {
    from { opacity: 0; }
    to { opacity: 1; }
}
dialog.cbtt-ui-modal[open] {
    animation: cbtt-ui-modal-fade-in 150ms ease;
}

.cbtt-ui-modal__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 16px 20px;
    border-bottom: 1px solid var(--cbtt-border-soft, #f0ebe1);
}
.cbtt-ui-modal__title {
    margin: 0;
    font-size: 17px;
    font-weight: 600;
}
.cbtt-ui-modal__close {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 0;
    border: 0;
    border-radius: var(--cbtt-radius-md, 11px);
    background: transparent;
    color: var(--cbtt-text-muted, #7c8494);
    cursor: pointer;
    transition: color 150ms ease, background-color 150ms ease;
}
.cbtt-ui-modal__close:hover {
    background: var(--cbtt-surface-alt, #f7f3ec);
    color: var(--cbtt-text, #1f2733);
}
.cbtt-ui-modal__close:focus-visible {
    outline: 2px solid var(--cbtt-teal, #0193aa);
    outline-offset: 2px;
    box-shadow: var(--cbtt-focus-ring, 0 0 0 3px rgba(1, 147, 170, 0.15));
}
.cbtt-ui-modal__body {
    padding: 20px;
}
.cbtt-ui-modal__footer {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    padding: 12px 20px;
    border-top: 1px solid var(--cbtt-border-soft, #f0ebe1);
}

/* ---- Collapse --------------------------------------------------------------
   <details class="cbtt-ui-collapse"><summary>…</summary><div class=
   "cbtt-ui-collapse__body">…</div></details>. The [open] state only ever
   recolors the summary and rotates its chevron — never touches the
   summary's own box, so nothing shifts on toggle; the visual divider between
   summary and body lives on __body's border-top instead. */

details.cbtt-ui-collapse {
    background: var(--cbtt-surface, #ffffff);
    border: 1px solid var(--cbtt-border, #e6e1d7);
    border-radius: var(--cbtt-radius-md, 11px);
}
details.cbtt-ui-collapse summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 12px 16px;
    list-style: none;
    font-weight: 600;
    color: var(--cbtt-text, #1f2733);
    cursor: pointer;
}
details.cbtt-ui-collapse summary::-webkit-details-marker {
    display: none;
}
details.cbtt-ui-collapse summary::after {
    content: "";
    flex: 0 0 auto;
    width: 8px;
    height: 8px;
    border-right: 2px solid var(--cbtt-text-muted, #7c8494);
    border-bottom: 2px solid var(--cbtt-text-muted, #7c8494);
    transform: rotate(45deg);
    transition: transform 150ms ease;
}
details.cbtt-ui-collapse[open] summary {
    color: var(--cbtt-accent-ink, #086b7e);
}
details.cbtt-ui-collapse[open] summary::after {
    border-color: var(--cbtt-accent-ink, #086b7e);
    transform: rotate(-135deg);
}
details.cbtt-ui-collapse summary:focus-visible {
    border-radius: var(--cbtt-radius-sm, 8px);
    outline: 2px solid var(--cbtt-teal, #0193aa);
    outline-offset: -2px;
    box-shadow: var(--cbtt-focus-ring, 0 0 0 3px rgba(1, 147, 170, 0.15));
}
details.cbtt-ui-collapse .cbtt-ui-collapse__body {
    padding: 4px 16px 16px;
    border-top: 1px solid var(--cbtt-border-soft, #f0ebe1);
}

/* ---- Utilities ------------------------------------------------------------ */

.cbtt-ui-d-none { display: none; }
.cbtt-ui-flex { display: flex; }
.cbtt-ui-align-center { align-items: center; }
.cbtt-ui-justify-end { justify-content: flex-end; }
.cbtt-ui-gap-1 { gap: 4px; }
.cbtt-ui-gap-2 { gap: 8px; }
.cbtt-ui-gap-4 { gap: 16px; }
.cbtt-ui-mt-0 { margin-top: 0; }
.cbtt-ui-mt-1 { margin-top: 4px; }
.cbtt-ui-mt-2 { margin-top: 8px; }
.cbtt-ui-mt-3 { margin-top: 12px; }
.cbtt-ui-mt-4 { margin-top: 16px; }
.cbtt-ui-mb-0 { margin-bottom: 0; }
.cbtt-ui-mb-1 { margin-bottom: 4px; }
.cbtt-ui-mb-2 { margin-bottom: 8px; }
.cbtt-ui-mb-3 { margin-bottom: 12px; }
.cbtt-ui-mb-4 { margin-bottom: 16px; }
.cbtt-ui-text-center { text-align: center; }
.cbtt-ui-text-end { text-align: right; }
.cbtt-ui-muted { color: var(--cbtt-text-muted, #7c8494); }
.cbtt-ui-w-100 { width: 100%; }

/* ---- Motion --------------------------------------------------------------
   One place to strip everything this file animates or transitions. */

@media (prefers-reduced-motion: reduce) {
    .cbtt-btn,
    .cbtt-ui-modal__close,
    .cbtt-ui-input,
    dialog.cbtt-ui-modal[open],
    details.cbtt-ui-collapse summary::after {
        transition: none;
        animation: none;
    }
}

/* ---- Check switch variant --------------------------------------------------
   Pill-shaped toggle for on/off rules (replaces Bootstrap's .form-switch).
   State changes color only, never geometry. */
.cbtt-ui-check--switch input[type="checkbox"] {
    appearance: none;
    -webkit-appearance: none;
    width: 36px;
    height: 20px;
    margin: 0;
    border: 1px solid var(--cbtt-border, #e6e1d7);
    border-radius: var(--cbtt-radius-pill, 999px);
    background: var(--cbtt-border, #e6e1d7);
    position: relative;
    cursor: pointer;
    transition: background-color 150ms ease, border-color 150ms ease;
    flex: 0 0 auto;
}

.cbtt-ui-check--switch input[type="checkbox"]::after {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: var(--cbtt-surface, #ffffff);
    transition: transform 150ms ease;
}

.cbtt-ui-check--switch input[type="checkbox"]:checked {
    background: var(--cbtt-teal, #0193aa);
    border-color: var(--cbtt-teal, #0193aa);
}

.cbtt-ui-check--switch input[type="checkbox"]:checked::after {
    transform: translateX(16px);
}

.cbtt-ui-check--switch input[type="checkbox"]:focus-visible {
    outline: 2px solid var(--cbtt-teal, #0193aa);
    outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
    .cbtt-ui-check--switch input[type="checkbox"],
    .cbtt-ui-check--switch input[type="checkbox"]::after {
        transition: none;
    }
}

/* Universal (admin-shell.css only defines this under body.cbtt-screen, which
   never exists on the frontend): wide tables scroll themselves, everywhere. */
.cbtt-table-scroll {
    overflow-x: auto;
}

/* ---- Loading spinner (replaces Bootstrap's .spinner-border) -------------- */
.cbtt-ui-spinner {
    display: inline-block;
    width: 1.25rem;
    height: 1.25rem;
    vertical-align: -0.125em;
    border: 0.2em solid currentColor;
    border-right-color: transparent;
    border-radius: 50%;
    animation: cbtt-ui-spin 0.75s linear infinite;
}

.cbtt-ui-spinner--sm {
    width: 1rem;
    height: 1rem;
    border-width: 0.15em;
}

@keyframes cbtt-ui-spin {
    to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
    .cbtt-ui-spinner { animation-duration: 1.5s; }
}

/* ---- Data tables (GDPR "My data" export, admin + [cbtt_my_data]) ----------
   Replaces the per-cell inline styles admin_gdpr_export.php carried until
   2026-08-29. --record: one field per row (label column + value);
   --list: several records as rows with a header line. */
.cbtt-my-data-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--cbtt-fs-sm, 0.875rem);
    color: var(--cbtt-text, #1f2733);
}

.cbtt-my-data-table th,
.cbtt-my-data-table td {
    text-align: left;
    vertical-align: top;
    padding: 6px 10px;
    border-bottom: 1px solid var(--cbtt-border-soft, #f0ebe1);
    word-break: break-word;
}

.cbtt-my-data-table th {
    white-space: nowrap;
    font-weight: var(--cbtt-fw-semibold, 600);
}

.cbtt-my-data-table--record {
    max-width: 800px;
    margin-bottom: 1em;
}

.cbtt-my-data-table--record th {
    padding: 4px 1em 4px 0;
    width: 1%;
}

.cbtt-my-data-table--record td {
    padding: 4px 0;
}

.cbtt-my-data-table--list thead th {
    border-bottom: 2px solid var(--cbtt-border, #e6e1d7);
}

/* ── Inline SVG icons ─────────────────────────────────────────────────────
   cbtt_suite_icon() (core/design/icons.php): outline glyphs that replace the
   old Unicode emoji. Sized in em so they follow the surrounding font-size,
   stroked in currentColor so they follow the surrounding text colour. */

.cbtt-icon {
    width: 1em;
    height: 1em;
    flex: none;
    vertical-align: -0.15em;
}

/* Tabs are inline-flex (admin-tabs.css), which swallows the whitespace text
   node between icon and label — restore the gap explicitly. */
.nav-tab .cbtt-icon {
    margin-right: 0.4em;
}

/* A small run of icons inside a table cell (timetable grid break cells). */
.cbtt-icon-row {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    vertical-align: middle;
}

.cbtt-icon-row .cbtt-icon {
    width: 1.25em;
    height: 1.25em;
}

/* ── Hint: circled "i" plus its explanation bubble ────────────────────────
   cbtt_suite_hint() (core/design/hint.php), behaviour in cbtt-ui.js.
   Replaces title="…" wherever an explanation matters: a native tooltip has no
   affordance and never opens on a touch device. */

.cbtt-hint {
    display: inline-flex;
    align-items: center;
}

/* Not a .cbtt-btn: this is an icon affordance sitting inside a label, and
   wp-admin's own button reset would give it a box, a border and a background.
   Geometry is identical in every state — DESIGN-SYSTEM rule 4 ("state changes
   only colour, never geometry"), which is also why the 24px box below is
   declared once and never grows on hover. */
.cbtt-hint__trigger {
    -webkit-appearance: none;
    appearance: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* 24×24 is the smallest target WCAG 2.5.8 accepts, and the icon itself is
       only ~1em — without this a finger would have to hit a 14px glyph. */
    width: 24px;
    height: 24px;
    padding: 0;
    margin: 0 0 0 var(--cbtt-space-1, 4px);
    border: 0;
    border-radius: var(--cbtt-radius-pill, 999px);
    background: none;
    color: var(--cbtt-text-muted, #7c8494);
    line-height: 0;
    cursor: pointer;
    flex: none;
}

/* Sized to the 24px box, not to the surrounding text: unlike every other
   .cbtt-icon this one is not a glyph inside a sentence, and the labels it sits
   next to run from 11px (card subhead) to 14px (table header) — following them
   would make the same control noticeably smaller on some rows than on others. */
.cbtt-hint__trigger .cbtt-icon {
    width: 15px;
    height: 15px;
    vertical-align: 0;
}

.cbtt-hint__trigger:hover,
.cbtt-hint__trigger:focus-visible,
.cbtt-hint__trigger[aria-expanded="true"] {
    color: var(--cbtt-primary, #002b57);
}

.cbtt-hint__trigger:focus {
    outline: none;
}

.cbtt-hint__trigger:focus-visible {
    box-shadow: var(--cbtt-focus-ring, 0 0 0 3px rgba(1, 147, 170, 0.15));
}

/* position:fixed and moved to <body> on first open (see cbtt-ui.js): a card
   with overflow:hidden — or any ancestor carrying a transform — would
   otherwise clip the bubble, and inside the student card grid that is exactly
   what happens.
   Every inherited text property is reset on purpose: the trigger usually sits
   in a small-caps, uppercased, 600-weight muted label, and none of that
   belongs in a sentence of prose. */
.cbtt-hint__bubble {
    position: fixed;
    left: 0;
    top: 0;
    z-index: 100001; /* above #wpadminbar (99999) */
    box-sizing: border-box;
    /* Reading width per DESIGN-SYSTEM "Lesebreite", but never wider than a
       phone: the second term wins on a narrow screen. */
    max-width: min(var(--cbtt-measure, 52ch), calc(100vw - 24px));
    padding: var(--cbtt-space-3, 12px) var(--cbtt-space-4, 16px);
    border: 1px solid var(--cbtt-border, #e6e1d7);
    border-radius: var(--cbtt-radius-md, 10px);
    background: var(--cbtt-surface, #ffffff);
    /* DESIGN-SYSTEM rule 3 keeps cards flat but allows a shadow for what
       genuinely floats — a popover is the case it names. */
    box-shadow: var(--cbtt-shadow-md, 0 8px 24px rgba(0, 43, 87, 0.06));
    color: var(--cbtt-text, #1f2733);
    font-family: inherit;
    font-size: var(--cbtt-fs-sm, 0.875rem);
    font-weight: 400;
    font-style: normal;
    line-height: 1.5;
    letter-spacing: normal;
    text-align: left;
    text-transform: none;
    white-space: normal;
    hyphens: none;
}

/* One refused field, explained under the input it belongs to
   (UX-GUIDELINES 4.5: at the field, what is wrong AND how to fix it).

   The "!" is part of the message, not decoration: colour alone would carry
   the whole meaning for anyone who cannot tell this red from the body text,
   which is exactly what 4.9 forbids. It is aria-hidden by construction — a
   CSS ::before is not in the accessibility tree — and the input points at
   the sentence with aria-describedby, so a screen reader reads the reason
   with the field either way. */
.cbtt-ui-field-error {
    display: flex;
    gap: var(--cbtt-space-2, 8px);
    margin: var(--cbtt-space-2, 8px) 0 0;
    color: var(--cbtt-error, #c4443a);
    font-size: var(--cbtt-fs-sm, 0.875rem);
    line-height: 1.5;
}

.cbtt-ui-field-error::before {
    content: "!";
    flex: 0 0 auto;
    width: 1.15em;
    height: 1.15em;
    border-radius: 50%;
    background: var(--cbtt-error, #c4443a);
    color: var(--cbtt-surface, #ffffff);
    font-weight: 700;
    line-height: 1.15em;
    text-align: center;
}

/* ---- Save bar ---------------------------------------------------------------
   A long form's own save row, floating at the bottom edge of the window while
   its real place is out of view: cbtt_suite_savebar_attrs()
   (core/design/savebar.php) marks the form, cbtt-ui.js puts this class on the
   row and takes it off again. Fixed, not sticky — savebar.php explains why.

   left and width are written by the script from the form's own box, so the
   bar lines up with the form it saves: in wp-admin that keeps it clear of the
   admin menu in every fold state without a single breakpoint here, and on a
   frontend page it follows the theme's content column. max-width and margin
   are reset inline for the same reason (the measure rule for settings-form
   paragraphs outranks any class selector).

   The row keeps its own display and its own children; only the frame is
   added. [data-cbtt-savebar] in the selector is specificity on purpose: the
   rows it floats bring their own position/background/padding rules
   (p.submit, .cbtt-settings-submit-row, .cbtt-save-bar …), all of them
   weaker than an attribute plus a class.

   Rounded top only — it stands on the window edge like a tab. The shadow is
   the exception DESIGN-SYSTEM rule 3 names for a sticky bar, same strength as
   the lesson list's action bar (cbtt-sm-shell-admin.css). Every colour is a
   token with its fallback, so the bar repaints with the preset and keeps a
   sane look under 'theme'. */
[data-cbtt-savebar] .cbtt-ui-savebar {
    position: fixed;
    bottom: 0;
    /* Over page content and sticky table headers, under the admin menu's
       fly-outs (9990), dropdown widgets and hint bubbles. */
    z-index: 1000;
    box-sizing: border-box;
    padding: var(--cbtt-space-3, 12px) var(--cbtt-space-4, 16px);
    padding-bottom: calc(var(--cbtt-space-3, 12px) + env(safe-area-inset-bottom, 0px));
    border: 1px solid var(--cbtt-border, #e6e1d7);
    border-bottom: 0;
    border-radius: var(--cbtt-radius-md, 11px) var(--cbtt-radius-md, 11px) 0 0;
    background: var(--cbtt-surface, #ffffff);
    box-shadow: 0 -2px 12px rgb(0 0 0 / 8%);
    color: var(--cbtt-text, #1f2733);
}

/* A row is often a group (cost note + buttons): its first and last child
   bring the vertical spacing they need in the form, not in the bar. */
[data-cbtt-savebar] .cbtt-ui-savebar > :first-child {
    margin-top: 0;
}

[data-cbtt-savebar] .cbtt-ui-savebar > :last-child {
    margin-bottom: 0;
}

/* submit_button() inside a marked wrapper: WordPress gives p.submit a top
   margin and padding to set it off from a form-table — in the bar that is
   only empty space above the button. */
[data-cbtt-savebar] .cbtt-ui-savebar p.submit {
    margin: 0;
    padding: 0;
}

@media print {
    [data-cbtt-savebar] .cbtt-ui-savebar {
        position: static;
        box-shadow: none;
    }
}

/* While the bar is up, anything scrolled into view — a focused field, an
   invalid one the browser jumps to — ends above it, not behind it
   (WCAG 2.4.11). The height comes from the script. */
html.cbtt-has-savebar {
    scroll-padding-bottom: var(--cbtt-savebar-h, 72px);
    /* CBTT-Turbo's accessibility button (fixed, bottom right) reads this
       variable; lift it above the bar while the bar is up, the way the
       student calendar's action bar does (cbtt-sm-shell.css). Measured on a
       390px theme page: without it the button sat on the bar's right end. */
    --cbtt-a11y-bottom: calc(var(--cbtt-savebar-h, 72px) + 16px);
}
