/* ============================================================
   Digiticks — Design System (Hebrew RTL, mobile-first)

   Ported from RetreatReg's design system (DECISIONS.md D17) with the
   token prefix changed from --rr-* to --dt-* and .rr-* to .dt-*.
   ============================================================ */


/* The typefaces are loaded by a <link> in each layout, not by an @import here.
   An @import cannot be discovered until this file has itself arrived, so the browser
   fetched the stylesheet, then the font CSS, then the font files — three hops in series
   before a single word could render in the right face. The link lets the first two happen
   at once, and a preconnect beside it takes the handshake off the second. */

/* ---------- Theme variables (event/company brand overrides --dt-primary) ---------- */
:root {
    /* Copper on white is 4.6:1 — the primary button is the one control that must always be
       readable, so the darker copper is the resting colour and the lighter one is for hover. */
    --dt-primary: #A6502D;
    --dt-primary-rgb: 166, 80, 45;
    --dt-primary-dark: #8A4225;
    --dt-primary-soft: rgba(var(--dt-primary-rgb), .1);
    --dt-secondary: #69717D;

    /* Champagne carries dark text, never white: #E8C49A is far too light to sit under it. */
    --dt-accent: #E8C49A;
    --dt-accent-strong: #D9A96F;
    --dt-accent-ink: #302A28;

    /* The warm ground behind highlighted areas — a KPI card, a callout. */
    --dt-cream: #FAF0E6;

    --dt-sidebar-bg: #302A28;
    --dt-sidebar-text: #D8D2CE;
    --dt-sidebar-muted: #9A918C;
    --dt-sidebar-hover: rgba(255, 255, 255, .07);
    --dt-sidebar-border: rgba(255, 255, 255, .09);

    --dt-success: #2E7D52;
    --dt-success-soft: #E3F3EA;
    --dt-warning: #A6631F;
    --dt-warning-soft: #FBF0DE;
    --dt-danger: #C0392B;
    --dt-danger-soft: #FBEAE8;
    --dt-info: #2C5A78;
    --dt-info-soft: #E6F0F6;

    --dt-bg: #F6F7F9;
    --dt-surface: #FFFFFF;
    /* The admin panel ground. A shade off white, so the white text boxes sitting on a panel
       are told apart from it by colour alone and need nothing else to separate them. The
       public pages keep pure white. */
    --dt-panel: #FCFCFB;
    --dt-text: #20232B;
    --dt-muted: #69717D;
    --dt-border: #E8EAEE;

    --dt-radius: .875rem;
    --dt-radius-sm: .5rem;
    /* Restrained rather than absent: enough to lift a card off the ground, not enough to
       announce itself. */
    --dt-shadow: 0 1px 2px rgba(32, 35, 43, .04), 0 8px 24px rgba(32, 35, 43, .06);
    --dt-shadow-sm: 0 1px 2px rgba(32, 35, 43, .06);

    --dt-sidebar-w: 240px;
    --dt-sidebar-w-collapsed: 72px;
    --dt-font: 'Heebo', 'Assistant', 'Segoe UI', 'Arial Hebrew', Arial, sans-serif;

    /* Worked out from the brand colour by the layout when an event sets one. The fallback
       is white, which is right for the copper above and wrong for a pale brand — which is
       exactly why the server computes it rather than the stylesheet assuming. */
    --dt-on-primary: #FFFFFF;
}

/* ---------- Semantic names ----------
   One vocabulary for the page to speak in, mapped onto the tokens the admin already writes.
   A component asks for --color-primary and gets whatever the event, or the company, or the
   product decided — in that order, because that is the order the layout emits them in.
   Nothing here introduces a second way to set a colour. */
:root {
    --color-primary: var(--dt-primary);
    --color-primary-dark: var(--dt-primary-dark);
    --color-primary-soft: var(--dt-primary-soft);
    --color-on-primary: var(--dt-on-primary);

    --color-background: var(--dt-bg);
    --color-surface: var(--dt-surface);
    --color-text: var(--dt-text);
    --color-text-muted: var(--dt-muted);
    --color-border: var(--dt-border);

    --color-danger: var(--dt-danger);
    --color-success: var(--dt-success);

    --radius-lg: 1.25rem;
    --radius: var(--dt-radius);
    --radius-sm: var(--dt-radius-sm);
    --transition: 170ms ease;
}

/* ---------- Bootstrap bridge ----------
   Bootstrap draws tabs, links, pagination and checkboxes from its own variables, so without
   this the tab bar stays default blue while everything around it is copper. Mapping them
   once is what keeps the palette from having to be repeated per component. */
:root {
    --bs-primary: var(--dt-primary);
    --bs-primary-rgb: var(--dt-primary-rgb);
    --bs-link-color: var(--dt-primary);
    --bs-link-color-rgb: var(--dt-primary-rgb);
    --bs-link-hover-color: var(--dt-primary-dark);
    --bs-border-color: var(--dt-border);
    --bs-body-color: var(--dt-text);
    --bs-secondary-color: var(--dt-muted);
    --bs-emphasis-color: var(--dt-text);
}

/* The selected tab carries champagne with dark ink, matching the selected menu item — the
   same idea of "you are here", said the same way. */
.nav-pills .nav-link.active,
.nav-pills .show > .nav-link {
    background-color: var(--dt-accent);
    color: var(--dt-accent-ink);
    font-weight: 600;
}

.nav-pills .nav-link { color: var(--dt-text); border-radius: 8px; }
.nav-pills .nav-link:hover:not(.active) { background: var(--dt-cream); color: var(--dt-primary-dark); }

.nav-tabs .nav-link.active { color: var(--dt-primary); border-bottom-color: var(--dt-surface); }

.progress { background: var(--dt-border); border-radius: 1rem; }
.progress-bar { background-color: var(--dt-primary); }

.page-item.active .page-link {
    background-color: var(--dt-primary);
    border-color: var(--dt-primary);
}
.page-link { color: var(--dt-primary); }

/* ---------- Dark theme (html[data-theme="dark"], toggled from the UI, saved in localStorage) ---------- */
html[data-theme="dark"] {
    /* Warm neutrals rather than blue-grey, so the dark work area belongs to the same palette
       as the sidebar instead of reading as a different product. */
    --dt-bg: #1B1817;
    --dt-surface: #262120;
    /* Dark already separates a field from its panel — the field goes darker than the
       surface, not lighter — so a panel is simply the surface here. */
    --dt-panel: var(--dt-surface);
    --dt-text: #EDE8E4;
    --dt-muted: #A79E99;
    --dt-border: #3A3331;

    /* Copper goes light on a dark ground; the resting colour would sink into it. */
    --dt-primary: #D4794C;
    --dt-primary-rgb: 212, 121, 76;
    --dt-primary-dark: #C06A40;

    --dt-cream: rgba(232, 196, 154, .08);

    --dt-success: #5FBF8B;
    --dt-success-soft: rgba(46, 125, 82, .22);
    --dt-warning: #E0A45C;
    --dt-warning-soft: rgba(166, 99, 31, .22);
    --dt-danger: #EC7C6F;
    --dt-danger-soft: rgba(192, 57, 43, .22);
    --dt-info: #7FB3D1;
    --dt-info-soft: rgba(44, 90, 120, .22);
    --dt-primary-soft: rgba(var(--dt-primary-rgb), .18);

    --dt-shadow: 0 1px 3px rgba(0, 0, 0, .35), 0 10px 30px rgba(0, 0, 0, .4);
    --dt-shadow-sm: 0 1px 2px rgba(0, 0, 0, .45);
}

html[data-theme="dark"] .dt-table thead th { background: rgba(255, 255, 255, .04); color: var(--dt-muted); }
html[data-theme="dark"] .dt-table tbody tr:hover { background: rgba(255, 255, 255, .04); }
/* Copper has to lighten on a dark ground to stay visible, and once it does, white text on
   it falls to 3.2:1. The ink goes dark instead — the same trade champagne already makes. */
html[data-theme="dark"] .btn-primary,
html[data-theme="dark"] .btn-primary:hover,
html[data-theme="dark"] .btn-primary:focus,
html[data-theme="dark"] .btn-outline-primary:hover,
html[data-theme="dark"] .progress-bar { color: #20232B; }

html[data-theme="dark"] .dt-badge-muted { background: #3A3331; color: #CFC7C2; }
html[data-theme="dark"] .dt-icon-btn { background: transparent; color: var(--dt-muted); }
html[data-theme="dark"] .dt-chart-placeholder { background: rgba(255, 255, 255, .03); }
html[data-theme="dark"] .dt-step::before { background: #3A3331; color: var(--dt-muted); }
html[data-theme="dark"] .dt-step:not(:last-child)::after { background: #3A3331; }
html[data-theme="dark"] .dt-roommate-chip { background: var(--dt-surface); border-color: var(--dt-border); }
html[data-theme="dark"] .dt-public-header,
html[data-theme="dark"] .dt-topbar,
html[data-theme="dark"] .dt-sticky-actions { background: var(--dt-surface); }
html[data-theme="dark"] .form-control,
html[data-theme="dark"] .form-select { background: #1B1817; border-color: var(--dt-border); color: var(--dt-text); }
html[data-theme="dark"] .form-control::placeholder { color: #8A817C; }
html[data-theme="dark"] .input-group-text { background: transparent; border-color: var(--dt-border); color: var(--dt-muted); }
html[data-theme="dark"] code { color: #93c5fd; }

/* ---------- Base ---------- */
html { scroll-behavior: smooth; }

body {
    font-family: var(--dt-font);
    background: var(--dt-bg);
    color: var(--dt-text);
    text-align: right;
    min-height: 100vh;
}

h1, h2, h3, h4, h5, h6 { font-weight: 700; }

a { color: var(--dt-primary); }
a:hover { color: var(--dt-primary-dark); }

/* ---------- Bootstrap overrides → premium look ---------- */
.btn {
    border-radius: var(--dt-radius-sm);
    font-weight: 600;
    padding: .55rem 1.25rem;
    /* iOS Safari: remove the tap delay + double-tap-to-zoom so a single tap always registers */
    touch-action: manipulation;
    -webkit-tap-highlight-color: rgba(0, 0, 0, .05);
    cursor: pointer;
}

.btn-lg { padding: .8rem 1.6rem; border-radius: var(--dt-radius-sm); }

.btn-primary {
    background: var(--dt-primary);
    border-color: var(--dt-primary);
    color: #fff;
    box-shadow: none;
}

.btn-primary:hover, .btn-primary:focus, .btn-primary:active {
    background: var(--dt-primary-dark) !important;
    border-color: var(--dt-primary-dark) !important;
}

.btn-outline-primary { color: var(--dt-primary); border-color: var(--dt-primary); }
.btn-outline-primary:hover { background: var(--dt-primary); border-color: var(--dt-primary); color: #fff; }

/* Champagne is far too light to sit under white text — it always carries dark ink. */
.btn-accent {
    background: var(--dt-accent);
    border-color: var(--dt-accent);
    color: var(--dt-accent-ink);
    font-weight: 600;
}
.btn-accent:hover, .btn-accent:focus {
    background: var(--dt-accent-strong);
    border-color: var(--dt-accent-strong);
    color: var(--dt-accent-ink);
}

.card {
    border: 0;
    border-radius: var(--dt-radius);
    box-shadow: var(--dt-shadow);
    background: var(--dt-surface);
}

.card-header {
    background: transparent;
    border-bottom: 1px solid var(--dt-border);
    font-weight: 700;
    padding: 1rem 1.5rem;
}

.card-body { padding: 1.5rem; }

/* Admin only: the KPI cards come along because they stand beside the plain ones on the
   dashboard, and one white panel in a row of off-white ones reads as a mistake. */
.dt-admin-wrapper .card,
.dt-admin-wrapper .dt-kpi {
    background: var(--dt-panel);
}

.form-control, .form-select {
    border-radius: var(--dt-radius-sm);
    border-color: var(--dt-border);
    padding: .65rem .9rem;
    font-size: 1rem;
}

/*
 * A select keeps the room its arrow is drawn in.
 *
 * Bootstrap paints the chevron as a background image and reserves space for it in the
 * padding — .375rem .75rem .375rem 2.25rem in the RTL build, the 2.25rem being the left,
 * which is where the arrow goes in Hebrew. The shorthand above sets all four sides, so it
 * replaced that 2.25rem with .9rem while the arrow went on being painted in the same
 * place: on top of the last character. Worst on a short, auto-width select — the rows-per-
 * page picker reads as a chevron struck through 50.
 *
 * Logical, not padding-left: the arrow is on the left in Hebrew and on the right in
 * English, and Bootstrap ships a separate build for each. One property follows both.
 */
.form-select {
    padding-inline-end: 2.25rem;
}

.form-control:focus, .form-select:focus {
    border-color: var(--dt-primary);
    box-shadow: 0 0 0 .2rem rgba(var(--dt-primary-rgb), .15);
}

.form-label { font-weight: 600; margin-bottom: .35rem; }
.form-text { color: var(--dt-muted); }

.form-check-input:checked { background-color: var(--dt-primary); border-color: var(--dt-primary); }

.alert { border: 0; border-radius: var(--dt-radius-sm); }

.dropdown-menu { border: 0; border-radius: var(--dt-radius-sm); box-shadow: var(--dt-shadow); }

/* ---------- Page header ---------- */
.dt-page-header {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 1.5rem;
}

.dt-page-header h1 { font-size: 1.45rem; margin: 0; }
.dt-page-header .dt-page-subtitle { color: var(--dt-muted); font-size: .9rem; margin: 0; }

/* Header actions — tidy rows.
   A screen like "עובדים" offers eight of these, so they wrap across two or
   three rows depending on the window. Two things made that look broken:

   1. A plain .btn is ~43.6px tall but a .form-select is ~46.8px, so any row
      holding the bulk "שנה זכאות" control stretched to the taller size while
      the rows without it stayed short — visibly ragged. One min-height for
      every control in the header keeps all rows equal, whatever wraps where.
   2. The primary action drifted: "עובד חדש" landed on row 3 at 1024px, row 2
      between 1280 and 1680, and row 1 at 1920. `order: -1` parks it at the
      head of the group — nearest the page title, same place at every width —
      so it is always where the eye already is.

   Note: `order` moves it visually only; keyboard focus still follows the
   document, which is the markup order. */
.dt-page-header > div:last-child:not(:only-child) .btn,
.dt-page-header > div:last-child:not(:only-child) .form-select,
.dt-page-header > div:last-child:not(:only-child) .input-group {
    min-height: 2.95rem;
}

.dt-page-header > div:last-child:not(:only-child) .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .35rem;
}

.dt-page-header > div:last-child:not(:only-child) > .btn-primary { order: -1; }

/* ---------- Admin shell: sidebar + main ----------
   The sidebar is fixed to the inline-start edge — the right in RTL, the left in LTR — and the
   work area is pushed over by the same width rather than sliding under it. Logical properties
   throughout, so the same rules serve both directions. */
.dt-admin-wrapper { min-height: 100vh; }

.dt-sidebar {
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    width: var(--dt-sidebar-w);
    background: var(--dt-sidebar-bg);
    color: var(--dt-sidebar-text);
    display: flex;
    flex-direction: column;
    z-index: 1040;
    transition: width .18s ease;
}

.dt-sidebar .dt-brand {
    display: flex;
    align-items: center;
    gap: .55rem;
    padding: 1.15rem 1.15rem 1rem;
    font-size: 1.35rem;
    font-weight: 800;
    letter-spacing: -.01em;
    color: #fff;
    text-decoration: none;
    flex: 0 0 auto;
}

/* digi in white, ticks in champagne — the logotype, until a real mark exists. */
.dt-brand-mark { white-space: nowrap; }
.dt-brand-mark .dt-brand-accent { color: var(--dt-accent); }
.dt-brand-tagline {
    display: block;
    font-size: .58rem;
    font-weight: 600;
    letter-spacing: .18em;
    color: var(--dt-sidebar-muted);
    margin-block-start: .1rem;
}

/* Only this scrolls when the menu is long, so the logo and the account block stay put. */
.dt-sidebar-nav {
    list-style: none;
    padding: .25rem .6rem 1rem;
    margin: 0;
    flex: 1 1 auto;
    overflow-y: auto;
    overscroll-behavior: contain;
}

.dt-sidebar-nav .dt-nav-section {
    font-size: .68rem;
    font-weight: 700;
    letter-spacing: .08em;
    color: var(--dt-sidebar-muted);
    padding: 1rem .75rem .35rem;
}

.dt-sidebar-nav a {
    display: flex;
    align-items: center;
    gap: .7rem;
    min-height: 46px;
    padding: .5rem .75rem;
    margin-bottom: .1rem;
    border-radius: 8px;
    color: var(--dt-sidebar-text);
    text-decoration: none;
    font-weight: 500;
    font-size: .93rem;
    transition: background .15s, color .15s;
}

.dt-sidebar-nav a .bi {
    font-size: 1.05rem;
    width: 1.35rem;
    flex: 0 0 1.35rem;
    text-align: center;
    color: var(--dt-sidebar-muted);
    transition: color .15s;
}

.dt-sidebar-nav a:hover { background: var(--dt-sidebar-hover); color: #fff; }
.dt-sidebar-nav a:hover .bi { color: var(--dt-accent); }

/* The selected screen: champagne ground with dark ink, which is the only legible pairing. */
.dt-sidebar-nav a.active {
    background: var(--dt-accent);
    color: var(--dt-accent-ink);
    font-weight: 600;
}
.dt-sidebar-nav a.active .bi { color: var(--dt-accent-ink); }

.dt-sidebar-nav a.disabled { color: #6B615C; pointer-events: none; }

/* The account block, held to the bottom and set off by a hairline. */
.dt-sidebar-foot {
    flex: 0 0 auto;
    border-block-start: 1px solid var(--dt-sidebar-border);
    padding: .75rem .6rem;
}

.dt-sidebar-user {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .4rem .5rem;
    min-width: 0;
}

.dt-sidebar-user .dt-avatar {
    width: 2rem;
    height: 2rem;
    flex: 0 0 2rem;
    border-radius: 50%;
    background: var(--dt-accent);
    color: var(--dt-accent-ink);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1rem;
}

.dt-sidebar-user .dt-user-lines { min-width: 0; line-height: 1.25; }
.dt-sidebar-user .dt-user-title {
    color: #fff;
    font-weight: 600;
    font-size: .88rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.dt-sidebar-user .dt-user-role {
    color: var(--dt-sidebar-muted);
    font-size: .74rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.dt-sidebar-foot .dt-foot-action {
    display: flex;
    align-items: center;
    gap: .7rem;
    width: 100%;
    min-height: 40px;
    padding: .4rem .75rem;
    border: 0;
    background: transparent;
    border-radius: 8px;
    color: var(--dt-sidebar-text);
    font-size: .88rem;
    text-align: start;
    text-decoration: none;
}
.dt-sidebar-foot .dt-foot-action .bi { width: 1.35rem; flex: 0 0 1.35rem; text-align: center; }
.dt-sidebar-foot .dt-foot-action:hover { background: var(--dt-sidebar-hover); color: #fff; }

/* ---------- Collapsed: icons only ---------- */
@media (min-width: 992px) {
    .dt-shell-collapsed .dt-sidebar { width: var(--dt-sidebar-w-collapsed); }
    .dt-shell-collapsed .dt-main { margin-inline-start: var(--dt-sidebar-w-collapsed); }

    .dt-shell-collapsed .dt-sidebar .dt-brand { justify-content: center; padding-inline: .5rem; }
    .dt-shell-collapsed .dt-brand-tagline,
    .dt-shell-collapsed .dt-sidebar-nav a > span,
    .dt-shell-collapsed .dt-sidebar-nav .dt-nav-section,
    .dt-shell-collapsed .dt-sidebar-user .dt-user-lines,
    .dt-shell-collapsed .dt-foot-action > span { display: none; }

    /* A rule of champagne stands in for the section heading, so the grouping survives the
       collapse instead of the items running together. */
    .dt-shell-collapsed .dt-sidebar-nav .dt-nav-section {
        display: block;
        height: 1px;
        padding: 0;
        margin: .6rem .75rem;
        background: var(--dt-sidebar-border);
        overflow: hidden;
        text-indent: -999em;
    }

    .dt-shell-collapsed .dt-sidebar-nav a,
    .dt-shell-collapsed .dt-foot-action,
    .dt-shell-collapsed .dt-sidebar-user { justify-content: center; padding-inline: .5rem; }
    .dt-shell-collapsed .dt-sidebar-nav a .bi { width: auto; flex: 0 0 auto; }
}

.dt-sidebar-toggle {
    border: 0;
    background: transparent;
    color: var(--dt-sidebar-muted);
    border-radius: 8px;
    width: 2rem;
    height: 2rem;
    display: none;
    align-items: center;
    justify-content: center;
    margin-inline-start: auto;
}
.dt-sidebar-toggle:hover { background: var(--dt-sidebar-hover); color: #fff; }
@media (min-width: 992px) { .dt-sidebar-toggle { display: flex; } }
.dt-shell-collapsed .dt-sidebar-toggle { margin-inline: auto; }

/* ---------- Tooltips for the collapsed menu ----------
   The name of an item that has lost its label, on hover and on keyboard focus alike — a menu
   reachable only by pointer would be no menu at all for anyone tabbing through it. */
@media (min-width: 992px) {
    .dt-shell-collapsed .dt-sidebar [data-dt-tip] { position: relative; }

    .dt-shell-collapsed .dt-sidebar [data-dt-tip]::after {
        content: attr(data-dt-tip);
        position: absolute;
        inset-inline-start: calc(100% + .5rem);
        top: 50%;
        transform: translateY(-50%);
        background: #16110F;
        color: #fff;
        font-size: .82rem;
        font-weight: 500;
        white-space: nowrap;
        padding: .3rem .6rem;
        border-radius: 6px;
        box-shadow: 0 4px 14px rgba(0, 0, 0, .3);
        opacity: 0;
        pointer-events: none;
        transition: opacity .12s ease;
        z-index: 1;
    }

    .dt-shell-collapsed .dt-sidebar [data-dt-tip]:hover::after,
    .dt-shell-collapsed .dt-sidebar [data-dt-tip]:focus-visible::after { opacity: 1; }
}

.dt-main {
    min-width: 0;
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    margin-inline-start: var(--dt-sidebar-w);
    transition: margin .18s ease;
}

.dt-topbar {
    background: var(--dt-surface);
    border-block-end: 1px solid var(--dt-border);
    padding: .7rem 1.5rem;
    display: flex;
    align-items: center;
    gap: 1rem;
    position: sticky;
    top: 0;
    z-index: 1020;
}

.dt-topbar .dt-context { font-weight: 600; font-size: .95rem; }
.dt-topbar .dt-context .text-muted { font-weight: 400; }

.dt-content { padding: 1.5rem; flex: 1 1 auto; }

@media (min-width: 992px) {
    .dt-content { padding: 2rem 2.5rem; }
}

/* ---------- Mobile: a drawer from the inline-start edge ---------- */
@media (max-width: 991.98px) {
    .dt-main { margin-inline-start: 0; }

    .dt-sidebar {
        width: min(82vw, 300px);
        transform: translateX(var(--dt-drawer-hidden, 100%));
        transition: transform .2s ease;
    }
    html[dir="ltr"] .dt-sidebar { --dt-drawer-hidden: -100%; }

    .dt-sidebar.is-open { transform: translateX(0); }

    .dt-drawer-backdrop {
        position: fixed;
        inset: 0;
        background: rgba(32, 42, 40, .5);
        z-index: 1035;
        opacity: 0;
        pointer-events: none;
        transition: opacity .2s ease;
    }
    .dt-drawer-backdrop.is-open { opacity: 1; pointer-events: auto; }

    body.dt-drawer-open { overflow: hidden; }
}

/* ---------- KPI cards ---------- */
.dt-kpi {
    background: var(--dt-surface);
    border-radius: var(--dt-radius);
    box-shadow: var(--dt-shadow);
    padding: 1.25rem 1.5rem;
    display: flex;
    align-items: center;
    gap: 1rem;
    height: 100%;
}

.dt-kpi .dt-kpi-icon {
    width: 3rem;
    height: 3rem;
    border-radius: .9rem;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.3rem;
    background: var(--dt-primary-soft);
    color: var(--dt-primary);
    flex-shrink: 0;
}

.dt-kpi .dt-kpi-value { font-size: 1.6rem; font-weight: 800; line-height: 1.1; }
.dt-kpi .dt-kpi-label { color: var(--dt-muted); font-size: .85rem; }

.dt-kpi.dt-kpi-success .dt-kpi-icon { background: var(--dt-success-soft); color: var(--dt-success); }
.dt-kpi.dt-kpi-warning .dt-kpi-icon { background: var(--dt-warning-soft); color: var(--dt-warning); }
.dt-kpi.dt-kpi-danger .dt-kpi-icon { background: var(--dt-danger-soft); color: var(--dt-danger); }

/* ---------- Status badges ---------- */
.dt-badge {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    border-radius: 2rem;
    padding: .3rem .8rem;
    font-size: .78rem;
    font-weight: 600;
    white-space: nowrap;
}

.dt-badge-success { background: var(--dt-success-soft); color: var(--dt-success); }
.dt-badge-warning { background: var(--dt-warning-soft); color: var(--dt-warning); }
.dt-badge-danger { background: var(--dt-danger-soft); color: var(--dt-danger); }
.dt-badge-info { background: var(--dt-info-soft); color: var(--dt-info); }
.dt-badge-muted { background: var(--dt-border); color: var(--dt-secondary); }

/* ---------- Modern tables ---------- */
.dt-table-card { overflow: hidden; }
.dt-table-card .dt-table-toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
    align-items: center;
    justify-content: space-between;
    padding: 1rem 1.5rem;
    border-bottom: 1px solid var(--dt-border);
}

.dt-table-card .dt-search { max-width: 280px; }

.dt-table { margin: 0; }

.dt-table thead th {
    background: var(--dt-bg);
    color: var(--dt-muted);
    font-size: .78rem;
    font-weight: 700;
    letter-spacing: .02em;
    border-bottom: 1px solid var(--dt-border);
    padding: .8rem 1.5rem;
    white-space: nowrap;
}

.dt-table tbody td {
    padding: .9rem 1.5rem;
    vertical-align: middle;
    border-color: var(--dt-border);
}

.dt-table tbody tr:hover { background: var(--dt-cream); }
.dt-table tbody tr:last-child td { border-bottom: 0; }

.dt-table-actions { display: flex; gap: .35rem; justify-content: flex-end; }

.dt-icon-btn {
    width: 2.1rem;
    height: 2.1rem;
    border-radius: .55rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--dt-border);
    background: var(--dt-surface);
    color: var(--dt-muted);
    transition: all .15s;
}

.dt-icon-btn:hover { border-color: var(--dt-primary); color: var(--dt-primary); background: var(--dt-primary-soft); }
.dt-icon-btn.dt-icon-btn-danger:hover { border-color: var(--dt-danger); color: var(--dt-danger); background: var(--dt-danger-soft); }
.dt-icon-btn.dt-icon-btn-warning { border-color: var(--dt-warning); color: var(--dt-warning); }
.dt-icon-btn.dt-icon-btn-warning:hover { border-color: var(--dt-warning); color: var(--dt-warning); background: var(--dt-warning-soft); }

/* Styled file dropzone */
.dt-dropzone {
    border: 2px dashed var(--dt-border);
    border-radius: 12px;
    padding: 22px 16px;
    text-align: center;
    cursor: pointer;
    transition: border-color .15s, background .15s;
    background: transparent;
    flex: 1 1 240px; /* fills width in a block container, and grows nicely inside a flex row */
}
.dt-dropzone:hover,
.dt-dropzone.dragover {
    border-color: var(--dt-primary);
    background: var(--dt-primary-soft);
}
.dt-dropzone .dt-dz-icon { font-size: 1.9rem; color: var(--dt-muted); }
.dt-dropzone .dt-dz-text { color: var(--dt-text); font-size: .92rem; margin-top: 4px; }
.dt-dropzone .dt-dz-hint { color: var(--dt-muted); font-size: .75rem; margin-top: 2px; }
.dt-dz-preview { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 10px; }
.dt-dz-file { display: flex; align-items: center; gap: 8px; }
.dt-dz-thumb { width: 54px; height: 54px; object-fit: cover; border-radius: 8px; border: 1px solid var(--dt-border); }
.dt-dz-name { font-weight: 600; word-break: break-all; }

/* Passport preview image inside a SweetAlert popup — shrink large scans to fit the screen */
.dt-passport-img {
    max-width: 80vw;
    max-height: 68vh;
    width: auto;
    height: auto;
    object-fit: contain;
    border-radius: 8px;
}

/* Admin footer */
.dt-footer {
    padding: 14px 24px;
    text-align: center;
    font-size: .8rem;
    color: var(--dt-muted);
    border-top: 1px solid var(--dt-border);
    margin-top: auto;
}

/* Top-bar company/event picker */
.dt-context-picker .form-select { min-width: 0; }
@media (max-width: 640px) {
    .dt-context-picker { width: 100%; }
    .dt-context-picker form { flex: 1 1 auto; }
    .dt-context-picker .form-select { width: 100%; }
}

/* ---------- Stepper (registration wizard) ---------- */
.dt-stepper {
    display: flex;
    justify-content: space-between;
    margin: 0 0 1.5rem;
    padding: 0;
    list-style: none;
    counter-reset: step;
}

.dt-step {
    flex: 1;
    text-align: center;
    position: relative;
    font-size: .72rem;
    color: var(--dt-muted);
}

.dt-step::before {
    counter-increment: step;
    content: counter(step);
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.1rem;
    height: 2.1rem;
    margin: 0 auto .35rem;
    border-radius: 50%;
    background: var(--dt-border);
    color: var(--dt-muted);
    font-weight: 700;
    font-size: .85rem;
    position: relative;
    z-index: 1;
}

/* connector line (RTL aware — each step draws the line back to the PREVIOUS step) */
.dt-step:not(:first-child)::after {
    content: '';
    position: absolute;
    top: 1.05rem;
    inset-inline-start: -50%;
    width: 100%;
    height: 2px;
    background: var(--dt-border);
}

.dt-step.done::before { background: var(--dt-success); color: #fff; content: '✓'; }
.dt-step.done:not(:first-child)::after,
.dt-step.active:not(:first-child)::after { background: var(--dt-success); }

.dt-step.active { color: var(--dt-primary); font-weight: 700; }
.dt-step.active::before {
    background: var(--dt-primary);
    color: #fff;
    box-shadow: 0 0 0 5px rgba(var(--dt-primary-rgb), .15);
}

@media (max-width: 575.98px) {
    .dt-step { font-size: .62rem; }
    .dt-step::before { width: 1.8rem; height: 1.8rem; }
    .dt-step:not(:first-child)::after { top: .9rem; }
}

/* Persistent "your chosen round" pill shown under the stepper on every step. */
.dt-round-pill {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    margin: 0 auto 1.25rem;
    padding: .4rem .9rem;
    border-radius: 2rem;
    background: var(--dt-primary-soft);
    color: var(--dt-primary-dark);
    border: 1px solid rgba(var(--dt-primary-rgb), .25);
    font-size: .9rem;
    font-weight: 600;
    max-width: 100%;
}
.dt-round-pill .bi { font-size: 1rem; }
.dt-round-pill-dates {
    padding: .1rem .5rem;
    border-radius: 1rem;
    background: #fff;
    color: var(--dt-primary-dark);
    font-weight: 700;
    font-size: .82rem;
}
.dt-round-pill-change {
    padding: .1rem .5rem;
    border-radius: 1rem;
    background: rgba(255, 255, 255, .25);
    color: #fff;
    font-weight: 600;
    font-size: .78rem;
    text-decoration: none;
    white-space: nowrap;
}
.dt-round-pill-change:hover { background: rgba(255, 255, 255, .4); color: #fff; }
.dt-round-pill-change .bi { font-size: .8rem; }
/* Center the pill as a block under the stepper. */
.dt-stepper + .dt-round-pill { display: flex; width: fit-content; }

/* ---------- Public registration ---------- */
.dt-public-body { background: var(--dt-bg); }

/* Event header banner — shown on every public registration page.
   Default mode: fill & crop (cover). --full modifier: whole image, natural height. */
.dt-header-banner {
    width: 100%;
    max-height: 220px;
    overflow: hidden;
    line-height: 0;
}

.dt-header-banner img {
    width: 100%;
    height: 100%;
    max-height: 220px;
    object-fit: cover;
}

@media (max-width: 767.98px) {
    .dt-header-banner:not(.dt-header-banner--full),
    .dt-header-banner:not(.dt-header-banner--full) img { max-height: 140px; }
}

.dt-header-banner--full,
.dt-header-banner--full img {
    max-height: none;
    height: auto;
    object-fit: unset;
}

.dt-public-header {
    background: var(--dt-surface);
    box-shadow: var(--dt-shadow-sm);
    padding: .8rem 1rem;
}

.dt-public-header .dt-logo {
    max-height: 42px;
    max-width: 140px;
    object-fit: contain;
}

.dt-hero {
    background: linear-gradient(135deg, var(--dt-primary), var(--dt-primary-dark));
    color: #fff;
    border-radius: var(--dt-radius);
    padding: 2rem 1.5rem;
    margin-bottom: 1.5rem;
    position: relative;
    overflow: hidden;
}

.dt-hero.dt-hero-image {
    background-size: cover;
    background-position: center;
}

.dt-hero.dt-hero-image::before {
    content: '';
    position: absolute;
    inset: 0;
    /* Overlay is configurable per-event via --dt-hero-overlay (set inline). Default = primary tint. */
    background: var(--dt-hero-overlay, linear-gradient(135deg, rgba(var(--dt-primary-rgb), .88), rgba(15, 23, 42, .55)));
}

.dt-hero > * { position: relative; }

.dt-hero .dt-hero-company { opacity: .85; font-size: .9rem; margin-bottom: .25rem; }
.dt-hero h1 { font-size: 1.6rem; font-weight: 800; margin-bottom: .75rem; }
.dt-hero .dt-hero-subtitle { font-size: 1.05rem; margin-top: -.4rem; margin-bottom: .75rem; }
.dt-hero .dt-hero-subtitle.dt-subtitle-sm { font-size: .9rem; }
.dt-hero .dt-hero-subtitle.dt-subtitle-md { font-size: 1.05rem; }
.dt-hero .dt-hero-subtitle.dt-subtitle-lg { font-size: 1.35rem; }
.dt-hero .dt-hero-subtitle.dt-subtitle-xl { font-size: 1.7rem; }

.dt-hero .dt-hero-meta {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem 1.25rem;
    font-size: .9rem;
    opacity: .95;
}

.dt-hero .dt-hero-meta .bi { margin-inline-end: .35rem; }

.dt-public-container {
    max-width: 640px;
    margin: 0 auto;
    padding: 1.25rem 1rem 6.5rem; /* bottom space keeps sticky bar clear of browser UI */
}

@media (min-width: 768px) {
    .dt-public-container { padding-top: 2rem; }
    .dt-hero { padding: 2.5rem 2rem; }
    .dt-hero h1 { font-size: 2rem; }
}

/* ---------- Mobile sticky action bar ---------- */
.dt-sticky-actions {
    position: fixed;
    bottom: 0;
    inset-inline: 0;
    background: var(--dt-surface);
    box-shadow: 0 -4px 20px rgba(15, 23, 42, .1);
    padding: .75rem 1rem calc(.75rem + env(safe-area-inset-bottom));
    z-index: 1030;
    touch-action: manipulation;
    /* keep the bar on its own compositor layer so iOS doesn't "settle" it on the first tap */
    transform: translateZ(0);
    -webkit-transform: translateZ(0);
}

.dt-sticky-actions .btn { width: 100%; }

@media (min-width: 768px) {
    .dt-sticky-actions { position: static; box-shadow: none; background: transparent; padding: 0; }
    .dt-sticky-actions .btn { width: auto; }
}

/* Touch-friendly buttons on mobile */
@media (max-width: 767.98px) {
    .dt-public-container .btn-primary,
    .dt-public-container .btn-outline-secondary { width: 100%; padding: .85rem 1rem; font-size: 1.05rem; }
}

/* ---------- Roommate selection ---------- */
.dt-roommate-chip {
    display: flex;
    align-items: center;
    gap: .75rem;
    background: var(--dt-surface);
    border: 1px solid var(--dt-border);
    border-radius: var(--dt-radius-sm);
    padding: .7rem 1rem;
    margin-bottom: .5rem;
}

.dt-roommate-chip .dt-avatar {
    width: 2.4rem;
    height: 2.4rem;
    border-radius: 50%;
    background: var(--dt-primary-soft);
    color: var(--dt-primary);
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    flex-shrink: 0;
}

.dt-roommate-chip .dt-roommate-name { font-weight: 600; }
.dt-roommate-chip .dt-roommate-meta { font-size: .8rem; color: var(--dt-muted); }
.dt-roommate-chip .dt-badge { margin-inline-start: auto; }

/* Member still needs to approve — highlighted in the admin rooms screen */
.dt-roommate-chip--pending {
    border-color: var(--dt-warning);
    background: var(--dt-warning-soft);
    box-shadow: inset 3px 0 0 var(--dt-warning);
}

/* ---------- Quick actions ---------- */
.dt-quick-actions { display: flex; flex-wrap: wrap; gap: .6rem; }

.dt-quick-actions .btn {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
}

/* ---------- Chart placeholder ---------- */
.dt-chart-placeholder {
    height: 260px;
    border: 2px dashed var(--dt-border);
    border-radius: var(--dt-radius-sm);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--dt-muted);
    background: var(--dt-bg);
}

/* ---------- Empty state ---------- */
/* Warm cream marks the areas that are set apart — an empty state, a callout — without
   introducing a second accent colour. */
.dt-empty {
    text-align: center;
    padding: 3rem 1.5rem;
    color: var(--dt-muted);
    background: var(--dt-cream);
    border: 1px solid var(--dt-border);
    border-radius: var(--dt-radius);
}

.dt-empty .bi { color: var(--dt-primary); }

.dt-empty .bi { font-size: 2.5rem; opacity: .5; display: block; margin-bottom: .75rem; }

/* The note callouts the screens use for "how this works" guidance. */
.alert-light { background: var(--dt-cream); color: var(--dt-text); }
html[data-theme="dark"] .alert-light { background: var(--dt-cream); color: var(--dt-text); }

/* ---------- Utilities ---------- */
.dt-text-muted { color: var(--dt-muted); }
.dt-surface { background: var(--dt-surface); }
code { direction: ltr; unicode-bidi: embed; }

/* ---------- Sign-in mark ---------- */
.dt-auth-mark {
    width: 4rem;
    height: 4rem;
    margin: 0 auto;
    border-radius: 1.25rem;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.9rem;
    color: #fff;
    background: linear-gradient(135deg, var(--dt-primary), var(--dt-primary-dark));
    box-shadow: 0 10px 30px rgba(var(--dt-primary-rgb), .35);
}

/* ---------- Topbar company switcher ----------
   The switcher sits in the light topbar, not a dark navbar, so it inherits the
   ordinary control styling rather than navbar text colours. */
.dt-company-pill {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    font-size: .85rem;
    font-weight: 600;
    color: var(--dt-muted);
    padding: .3rem .7rem;
    border-radius: 999px;
    background: var(--dt-primary-soft);
    white-space: nowrap;
}

@media (max-width: 575.98px) {
    .dt-company-pill span { display: none; }
    .dt-user-name { display: none; }
}

/* ---------- Focus ----------
   A keyboard user must always be able to see where they are. :focus-visible keeps the
   ring off mouse clicks, so it never becomes visual noise for pointer users. */
:focus-visible {
    outline: 2px solid var(--dt-primary);
    outline-offset: 2px;
    border-radius: .35rem;
}

.btn:focus-visible,
.dropdown-item:focus-visible,
.dt-sidebar-nav a:focus-visible,
.dt-bottom-nav a:focus-visible,
.dt-bottom-nav button:focus-visible,
.nav-link:focus-visible,
.page-link:focus-visible {
    outline: 2px solid var(--dt-primary);
    outline-offset: 2px;
    box-shadow: 0 0 0 4px var(--dt-primary-soft);
}

/* On a dark sidebar the brand colour has too little contrast to read as a ring. */
.dt-sidebar-nav a:focus-visible,
.dt-brand:focus-visible {
    outline-color: #fff;
    box-shadow: 0 0 0 4px rgba(255, 255, 255, .25);
}

/* Skip link — the first thing a keyboard user reaches, hidden until focused. */
.dt-skip-link {
    position: absolute;
    inset-inline-start: 1rem;
    top: -4rem;
    z-index: 1100;
    padding: .6rem 1rem;
    border-radius: var(--dt-radius-sm);
    background: var(--dt-primary);
    color: #fff;
    font-weight: 600;
    transition: top .15s ease;
}

.dt-skip-link:focus {
    top: 1rem;
    color: #fff;
}

/* ---------- Status pages ---------- */
.dt-status-page {
    min-height: 60vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 2rem 1rem;
}

.dt-status-page .dt-status-code {
    font-size: 4rem;
    font-weight: 800;
    line-height: 1;
    color: var(--dt-primary);
    opacity: .35;
}

.dt-status-page h1 { margin: 1rem 0 .5rem; font-size: 1.35rem; }
.dt-status-page p { color: var(--dt-muted); max-width: 34rem; }

/* Reduced motion: honour the OS preference rather than animating regardless. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
        scroll-behavior: auto !important;
    }
}

/* ---------------------------------------------------------------------------
   Ticket card — the thing a guest holds at the door.

   Drawn identically in the appearance editor's preview, on the public order
   page and in the admin, because all three render _TicketCard.cshtml. Sizes
   are in px rather than rem: a ticket is a fixed artefact, and it must not
   reflow when the surrounding page changes its root font size.
   --------------------------------------------------------------------------- */
.dt-ticket {
    position: relative;
    width: 100%;
    max-width: var(--dt-ticket-width, 340px);
    aspect-ratio: var(--dt-ticket-ratio, 0.625);
    margin-inline: auto;
    border-radius: 14px;
    overflow: hidden;
    background: var(--dt-ticket-bg, #fff);
    color: var(--dt-ticket-text, #0f172a);
    /* The card writes the variable from the appearance; the value here is the shadow the
       card was designed with, for anything that renders the partial without it. */
    box-shadow: var(--dt-card-shadow, 0 6px 20px rgb(15 23 42 / 18%));

    /* One base the card's type is measured against. Everything inside sizes itself in em,
       so the scale moves the card together instead of pulling one line out of proportion. */
    font-family: var(--dt-ticket-font, inherit);
    font-size: calc(1rem * var(--dt-ticket-font-scale, 1));
    font-weight: var(--dt-ticket-weight, 400);
}

/* The shape only sets the default; --dt-ticket-width carries the organiser's choice. */
.dt-ticket-landscape { max-width: var(--dt-ticket-width, 520px); }

/* The banner: a band across the top, behind the body but above the background image. */
.dt-ticket-header {
    position: absolute;
    inset-inline: 0;
    top: 0;
    background-position: center;
    background-size: cover;
    background-repeat: no-repeat;
}

.dt-ticket-image,
.dt-ticket-overlay {
    position: absolute;
    inset: 0;
}

.dt-ticket-image { background-position: center; background-repeat: no-repeat; }

.dt-ticket-body {
    position: relative;   /* above the image and its overlay */
    height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: 10px;
    /* Named so a full-bleed band can cancel exactly this much and reach both edges. */
    --dt-ticket-pad: 18px;
    padding: var(--dt-ticket-pad);
    text-align: center;
    min-height: 0;        /* lets the code shrink instead of overflowing the card */
}

/* When the rows set the height there is nothing to distribute, and space-between would
   push the code and the rows apart to fill a card that no longer has a fixed height. */
.dt-ticket-auto .dt-ticket-body {
    height: auto;
    justify-content: flex-start;
}

/* The code was placed at the top, so the details move below it. */
.dt-ticket-code-first { flex-direction: column-reverse; justify-content: flex-end; }

/* Reversing the body also reverses nothing inside the details, which is what we want: the
   code moves above the facts while the facts keep their own order. With banded rows the
   card grows downward, so the reversed body must start at the top rather than the end. */
.dt-ticket-auto .dt-ticket-code-first { justify-content: flex-start; }

.dt-ticket-logo {
    max-height: 44px;
    max-width: 60%;
    object-fit: contain;
    margin-bottom: 8px;
}

.dt-ticket-title {
    font-size: 1.19em;
    font-weight: 700;
    line-height: 1.25;
    margin-bottom: 6px;
}

.dt-ticket-line {
    font-size: .81em;
    line-height: 1.5;
    opacity: .9;
}

/* The notice a visitor meets when the sale is closed. Calm rather than alarming: for most
   of the people who see it, nothing is wrong — they came for a ticket they already hold. */
.dt-closed-notice {
    text-align: center;
}

.dt-closed-notice .dt-panel-title {
    justify-content: center;
}

/* The chosen size is a maximum, not a promise. A code larger than the card can hold
   shrinks to fit: the card clips its overflow, and a clipped code does not scan. */
.dt-ticket-code {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    min-height: 0;
    flex: 0 1 auto;
}

.dt-ticket-qr-panel { min-height: 0; }

.dt-ticket-code img,
.dt-ticket-qr-panel img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

/* A scanner reads contrast. A code laid straight onto a photograph often will not
   read at all, so the panel is the default. */
/* The padding is set inline from the code's size: it is the quiet zone the QR
   specification requires, not decoration, so it scales with the symbol. */
.dt-ticket-qr-panel {
    background: #fff;
    border-radius: 10px;
    line-height: 0;
    display: flex;
    min-height: 0;
}

/* A QR read as white-on-black fails on many scanners, so the code is held out of every
   colour transform the platform might apply: forced-colours modes, Windows High Contrast,
   and the print pipeline. The page-level defence is the color-scheme declaration in
   _PublicLayout; this is the belt to that pair of braces.

   An operating-system inversion (MIUI's system dark theme, Android's accessibility colour
   inversion) happens below the browser and cannot be refused from here — which is why the
   printed digits under the code are on by default. */
.dt-ticket-qr-panel,
.dt-ticket-qr-bare,
.dt-ticket-qr-panel img,
.dt-ticket-qr-bare img {
    forced-color-adjust: none;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
}

/* No panel chosen: the margin still has to exist, so it comes from the card itself. */
.dt-ticket-qr-bare {
    line-height: 0;
    display: flex;
    min-height: 0;
}

.dt-ticket-code img { display: block; }

.dt-ticket-codetext {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: .81em;
    letter-spacing: .08em;
}

.dt-ticket-qr-placeholder {
    display: grid;
    place-items: center;
    border: 2px dashed currentColor;
    border-radius: 10px;
    opacity: .35;
    font-size: 40px;
}

.dt-ticket-footer {
    font-size: .69em;
    line-height: 1.4;
    opacity: .75;
}

/* ---------------------------------------------------------------------------
   Quantity picker — public pages.

   Two shapes over one control: a joined pill, or round buttons spaced either
   side of the number. Both are driven by the same number input, which is what
   posts, so the page still works if the script never loads.
   --------------------------------------------------------------------------- */
.dt-qty {
    display: inline-flex;
    align-items: center;
    direction: ltr;   /* − always left of +, whichever way the page reads */
}

.dt-qty-value {
    width: 2.75rem;
    border: 0;
    background: transparent;
    text-align: center;
    font-weight: 600;
    font-size: 1rem;
    color: inherit;
    padding: 0;
    -moz-appearance: textfield;
}

/* The native spinners duplicate the buttons and are far too small to hit. */
.dt-qty-value::-webkit-outer-spin-button,
.dt-qty-value::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.dt-qty-value:focus-visible {
    outline: 2px solid var(--dt-primary, #A6502D);
    outline-offset: 2px;
    border-radius: 4px;
}

.dt-qty-btn {
    display: grid;
    place-items: center;
    /* 2.25rem keeps the target at roughly 36px, which is a comfortable thumb. */
    width: 2.25rem;
    height: 2.25rem;
    border: 0;
    background: transparent;
    color: var(--dt-primary, #A6502D);
    font-size: 1.15rem;
    line-height: 1;
    cursor: pointer;
    transition: background-color .12s ease, opacity .12s ease;
}

.dt-qty-btn:hover:not(:disabled) { background: rgb(15 118 110 / 10%); }
.dt-qty-btn:disabled { opacity: .3; cursor: default; }

/* --- pill: one joined control ------------------------------------------- */
.dt-qty-pill {
    border: 1px solid var(--dt-border, #E8EAEE);
    border-radius: 999px;
    background: var(--dt-surface, #fff);
    overflow: hidden;
}

.dt-qty-pill .dt-qty-btn:first-child { border-inline-end: 1px solid var(--dt-border, #E8EAEE); }
.dt-qty-pill .dt-qty-btn:last-child { border-inline-start: 1px solid var(--dt-border, #E8EAEE); }

/* --- circles: separate buttons ------------------------------------------- */
.dt-qty-circles { gap: .4rem; }

.dt-qty-circles .dt-qty-btn {
    border: 1px solid var(--dt-border, #E8EAEE);
    border-radius: 50%;
    background: var(--dt-surface, #fff);
}

.dt-qty-circles .dt-qty-value {
    border: 1px solid transparent;
}

/* ---------------------------------------------------------------------------
   Committee picker — an event that sells through several unions.
   --------------------------------------------------------------------------- */
.dt-org-card {
    overflow: hidden;
    transition: transform .12s ease, box-shadow .12s ease;
}

a.dt-org-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 20px rgb(15 23 42 / 14%);
}

/* The name sits on the brand colour, so the grid reads as one set of choices
   rather than as a scatter of unrelated logos. */
.dt-org-name {
    background: var(--dt-primary, #A6502D);
    color: #fff;
    font-size: .95rem;
}

/* ---------- Ticket type tints ----------
   A pastel per ticket type, so the products on a page are told apart at a glance rather than
   by reading. The colour is derived from the type's id and never stored, so it is stable for
   the life of the type and nobody has to choose one.

   Eight tints, because past that the pastels stop being distinguishable and the colour starts
   lying about being meaningful. Colour is never the only signal — the name is always there. */
.dt-type-tint {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    border-radius: .5rem;
    padding: .25rem .6rem;
    font-weight: 600;
    background: var(--dt-tint-bg);
    color: var(--dt-tint-fg);
    border: 1px solid var(--dt-tint-border);
}

/* A heading-sized block for a card that is about one type. */
h1.dt-type-tint, h2.dt-type-tint, h3.dt-type-tint { padding: .4rem .75rem; }

.dt-type-0 { --dt-tint-bg: #e0f2fe; --dt-tint-fg: #075985; --dt-tint-border: #bae6fd; }
.dt-type-1 { --dt-tint-bg: #dcfce7; --dt-tint-fg: #166534; --dt-tint-border: #bbf7d0; }
.dt-type-2 { --dt-tint-bg: #fef3c7; --dt-tint-fg: #854d0e; --dt-tint-border: #fde68a; }
.dt-type-3 { --dt-tint-bg: #fae8ff; --dt-tint-fg: #86198f; --dt-tint-border: #f5d0fe; }
.dt-type-4 { --dt-tint-bg: #ffe4e6; --dt-tint-fg: #9f1239; --dt-tint-border: #fecdd3; }
.dt-type-5 { --dt-tint-bg: #e0e7ff; --dt-tint-fg: #3730a3; --dt-tint-border: #c7d2fe; }
.dt-type-6 { --dt-tint-bg: #ccfbf1; --dt-tint-fg: #115e59; --dt-tint-border: #99f6e4; }
.dt-type-7 { --dt-tint-bg: #ffedd5; --dt-tint-fg: #9a3412; --dt-tint-border: #fed7aa; }

/* Pastels on a dark surface read as washed-out and lose their contrast, so dark mode keeps the
   hue and inverts the relationship: a deep, desaturated ground with a light tint on top. */
html[data-theme="dark"] .dt-type-0 { --dt-tint-bg: #0c4a6e33; --dt-tint-fg: #7dd3fc; --dt-tint-border: #075985; }
html[data-theme="dark"] .dt-type-1 { --dt-tint-bg: #14532d33; --dt-tint-fg: #86efac; --dt-tint-border: #166534; }
html[data-theme="dark"] .dt-type-2 { --dt-tint-bg: #713f1233; --dt-tint-fg: #fcd34d; --dt-tint-border: #854d0e; }
html[data-theme="dark"] .dt-type-3 { --dt-tint-bg: #701a7533; --dt-tint-fg: #f0abfc; --dt-tint-border: #86198f; }
html[data-theme="dark"] .dt-type-4 { --dt-tint-bg: #88134333; --dt-tint-fg: #fda4af; --dt-tint-border: #9f1239; }
html[data-theme="dark"] .dt-type-5 { --dt-tint-bg: #31297033; --dt-tint-fg: #a5b4fc; --dt-tint-border: #3730a3; }
html[data-theme="dark"] .dt-type-6 { --dt-tint-bg: #134e4a33; --dt-tint-fg: #5eead4; --dt-tint-border: #115e59; }
html[data-theme="dark"] .dt-type-7 { --dt-tint-bg: #7c2d1233; --dt-tint-fg: #fdba74; --dt-tint-border: #9a3412; }

/* ---------- Ticket: banded detail rows ----------
   The printed-ticket look: each fact on its own full-width band, scanned down the card
   rather than read across. The icons line up in a column at the leading edge, which is what
   makes the list readable at a glance in a queue. */
.dt-ticket-rows {
    display: flex;
    flex-direction: column;
    margin-block-start: .6em;
    /* Full bleed: the body's padding is cancelled so a band reaches both edges. */
    margin-inline: calc(var(--dt-ticket-pad, 16px) * -1);
}

.dt-ticket-row {
    display: flex;
    align-items: center;

    /* The card centres its text. With the label stretched across the row, that centred
       the words in the leftover space and left the icon stranded at the far edge with a
       gap between them — the icon read as belonging to nothing. Centring the pair keeps
       them together, which is what a row of icon-and-fact is. */
    justify-content: center;

    gap: .7em;
    padding: .55em var(--dt-ticket-pad, 16px);
    font-size: .9em;
    line-height: 1.35;
}

/* Every other band carries a wash rather than a border, so the rows separate without
   drawing a grid over the card. The wash is the text colour at low alpha, which keeps it
   working on a dark card as well as a light one. */
.dt-ticket-row:nth-child(odd) { background: rgb(from currentColor r g b / 6%); }

.dt-ticket-row .bi {
    flex: 0 0 1.4em;
    font-size: 1.1em;
    text-align: center;
    opacity: .75;
}

.dt-ticket-row span {
    /* Shrink to the words, not to the row: growing is what pushed the icon away. It may
       still shrink, so a long venue name wraps inside the band instead of widening it. */
    flex: 0 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
}

/* A fixed shape would cut a row off rather than let the card grow. */
.dt-ticket-auto {
    aspect-ratio: auto;
    min-height: 0;
}

/* ---------- Payment dialog header ----------
   Green because that is what a buyer reads as safe at the moment they type a card number.
   The shade is the palette's own success colour, which already carries white text at AA. */
.dt-pay-header {
    background: var(--dt-success);
    color: #fff;
    border-bottom: 0;
}

.dt-pay-header .modal-title { color: #fff; }
.dt-pay-header .bi { opacity: .95; }

/* The provider's form starts at its own first field with no margin of its own, so without
   this the card number sits against the green band and reads as cut off. Top only — the
   form manages its own sides, and padding there would narrow it on a phone. */
.dt-pay-body {
    padding: 1rem 0 0;
    background: #fff;
}

/* ---------- Add to phone wallet ----------
   Each wallet asks for its own colour, and both are dark with white type, so they read as
   the platform's own control rather than as ours. */
.dt-wallet-buttons {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    justify-content: center;
    margin-top: .75rem;
}

.dt-wallet-btn {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    padding: .55rem 1.1rem;
    border-radius: 2rem;
    font-size: .85rem;
    font-weight: 600;
    text-decoration: none;
    color: #fff;
    background: #202124;
    border: 1px solid rgba(255, 255, 255, .15);
}

.dt-wallet-btn:hover { color: #fff; opacity: .9; }
.dt-wallet-btn .bi { font-size: 1.1rem; }

.dt-wallet-apple { background: #000; }

/* ============================================================
   The public checkout

   Every colour below is a semantic variable, so an event that sets its own brand gets this
   layout in its own colours and nothing here has to know what they are. What stays the same
   between events is the shape: the hierarchy, the spacing, the radii, the focus rings.
   ============================================================ */

.dt-checkout { max-width: 1000px; margin-inline: auto; }

/* ---------- Hero ---------- */

.dt-co-hero {
    position: relative;
    overflow: hidden;
    border-radius: var(--radius-lg);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    margin-bottom: 1.25rem;
}

.dt-co-hero-grid {
    display: grid;
    grid-template-columns: 1fr;
    align-items: stretch;
}

@media (min-width: 768px) {
    .dt-co-hero-grid { grid-template-columns: 1.1fr .9fr; }
    .dt-co-hero-art { order: 2; }
}

.dt-co-hero-body { padding: 1.75rem 1.5rem; }

@media (min-width: 768px) {
    .dt-co-hero-body { padding: 2.5rem 2.25rem; }
}

.dt-co-hero-eyebrow {
    display: inline-block;
    padding: .3rem .85rem;
    border-radius: 999px;
    background: var(--color-primary-soft);
    color: var(--color-primary-dark);
    font-size: .8125rem;
    font-weight: 600;
}

.dt-co-hero-title {
    font-size: clamp(1.75rem, 5vw, 2.75rem);
    font-weight: 800;
    line-height: 1.15;
    color: var(--color-text);
    margin: .75rem 0 .5rem;
}

.dt-co-hero-lead { color: var(--color-text-muted); margin-bottom: 1.25rem; }

.dt-co-hero-facts { display: flex; flex-wrap: wrap; gap: .5rem; }

.dt-fact {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    padding: .5rem .9rem;
    border: 1px solid var(--color-border);
    border-radius: 999px;
    background: var(--color-surface);
    color: var(--color-text);
    font-size: .9rem;
}

.dt-fact .bi { color: var(--color-primary); }

/* The artwork sits behind nothing: the words are always real text beside it, never on it,
   so a dark photograph cannot take the title with it. */
/* Shorter on a phone, and shorter still when there is no photograph: the opening is
   scenery and the tickets are the errand, so the errand should be reachable with one
   thumb-flick rather than three. */
.dt-co-hero-art { position: relative; min-height: 140px; background: var(--color-primary-soft); }

.dt-co-hero-art--blank { min-height: 96px; }

.dt-co-hero-art img { width: 100%; height: 100%; object-fit: cover; display: block; }

@media (min-width: 768px) {
    .dt-co-hero-art, .dt-co-hero-art--blank { min-height: 260px; }
}

/* Drawn rather than uploaded, for an event with no picture: the brand colour at low
   opacity, which is a deliberate surface rather than an empty box. */
.dt-co-hero-art--blank {
    background:
        radial-gradient(120% 90% at 20% 15%, rgba(var(--dt-primary-rgb), .22), transparent 60%),
        radial-gradient(110% 80% at 85% 80%, rgba(var(--dt-primary-rgb), .14), transparent 55%),
        var(--color-surface);
}

.dt-co-hero-art--blank .bi {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    font-size: 2.25rem;
    color: rgba(var(--dt-primary-rgb), .35);
}

@media (min-width: 768px) {
    .dt-co-hero-art--blank .bi { font-size: 3.5rem; }
}

/* ---------- Steps ---------- */

.dt-co-steps {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-bottom: 1.25rem;
    padding: 0;
    list-style: none;
}

.dt-co-step { display: flex; align-items: center; gap: .5rem; flex: 0 0 auto; }

.dt-co-step-dot {
    width: 1.9rem;
    height: 1.9rem;
    border-radius: 999px;
    display: grid;
    place-items: center;
    font-size: .875rem;
    font-weight: 700;
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-text-muted);
    transition: background var(--transition), color var(--transition), border-color var(--transition);
}

.dt-co-step-label { font-size: .875rem; color: var(--color-text-muted); }

.dt-co-step-rule { flex: 1 1 auto; height: 1px; background: var(--color-border); min-width: 1rem; }

.dt-co-step[aria-current="step"] .dt-co-step-dot {
    background: var(--color-primary);
    border-color: var(--color-primary);
    color: var(--color-on-primary);
}

.dt-co-step[aria-current="step"] .dt-co-step-label { color: var(--color-primary-dark); font-weight: 600; }

.dt-co-step[data-state="done"] .dt-co-step-dot {
    background: var(--color-primary-soft);
    border-color: transparent;
    color: var(--color-primary-dark);
}

@media (max-width: 575.98px) {
    .dt-co-step:not([aria-current="step"]) .dt-co-step-label { display: none; }
}

/* ---------- Cards ---------- */

.dt-panel {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    padding: 1.25rem;
    margin-bottom: 1rem;
}

@media (min-width: 768px) {
    .dt-panel { padding: 1.75rem; }
}

.dt-panel-title {
    display: flex;
    align-items: center;
    gap: .6rem;
    font-size: 1.0625rem;
    font-weight: 700;
    color: var(--color-text);
    margin-bottom: 1.125rem;
}

.dt-panel-title .bi { color: var(--color-primary); font-size: 1.15rem; }

/* ---------- Ticket rows ---------- */

/* Raised tiles rather than outlined boxes: on a white panel a border alone makes a row
   look like a table, and these are things to press. The shadow does the separating, so
   the border can be faint enough to disappear. */
.dt-co-ticket-row {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: .9rem 1rem;
    background: var(--color-surface);
    border: 1px solid color-mix(in srgb, var(--color-border) 60%, var(--color-surface));
    border-radius: var(--radius);
    margin-bottom: .75rem;
    box-shadow: 0 1px 2px rgba(0, 0, 0, .04), 0 6px 16px rgba(0, 0, 0, .05);
    transition: border-color var(--transition), background var(--transition),
                box-shadow var(--transition);
}

.dt-co-ticket-row:hover {
    box-shadow: 0 1px 2px rgba(0, 0, 0, .05), 0 10px 22px rgba(0, 0, 0, .07);
}

.dt-co-ticket-row:last-child { margin-bottom: 0; }

/* A chosen row says so quietly — the border and a wash, not a colour change that would
   fight whatever brand the event is in. */
/* Opaque, not a translucent wash: the picker inside sits on its own tone, and two
   translucent layers would compound into a third colour neither of them chose. */
.dt-co-ticket-row.is-chosen {
    border-color: var(--color-primary);
    background: color-mix(in srgb, var(--color-primary) 9%, var(--color-surface));
    box-shadow: 0 1px 2px rgba(var(--dt-primary-rgb), .12),
                0 8px 20px rgba(var(--dt-primary-rgb), .14);
}

.dt-co-ticket-icon {
    flex: 0 0 auto;
    width: 2.75rem;
    height: 2.75rem;
    border-radius: var(--radius-sm);
    display: grid;
    place-items: center;
    background: var(--color-primary-soft);
    color: var(--color-primary-dark);
    font-size: 1.25rem;
}

.dt-co-ticket-main { flex: 1 1 auto; min-width: 0; }

.dt-co-ticket-name { font-weight: 600; color: var(--color-text); }

.dt-co-ticket-note { font-size: .875rem; color: var(--color-text-muted); }

.dt-co-ticket-price { font-size: .9375rem; color: var(--color-text); font-weight: 600; }

.dt-co-ticket-price--free { color: var(--color-text-muted); font-weight: 500; }

/* ---------- Fields ---------- */

.dt-field-label {
    display: block;
    font-size: .875rem;
    font-weight: 600;
    color: var(--color-text);
    margin-bottom: .35rem;
}

.dt-checkout .form-control,
.dt-checkout .form-select {
    min-height: 3rem;
    border-radius: var(--radius-sm);
    border-color: var(--color-border);
    background: var(--color-surface);
    color: var(--color-text);
    transition: border-color var(--transition), box-shadow var(--transition);
}

.dt-checkout .form-control:focus,
.dt-checkout .form-select:focus,
.dt-checkout .btn:focus-visible {
    border-color: var(--color-primary);
    box-shadow: 0 0 0 .2rem rgba(var(--dt-primary-rgb), .25);
    outline: none;
}

/* ---------- Coupon ---------- */

.dt-coupon-toggle {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    background: none;
    border: 0;
    padding: 0;
    color: var(--color-primary-dark);
    font-weight: 600;
    font-size: .9375rem;
}

.dt-coupon-toggle .bi { transition: transform var(--transition); }

.dt-coupon-toggle[aria-expanded="true"] .bi { transform: rotate(180deg); }

/* ---------- Summary ---------- */

.dt-summary-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 1rem;
    padding: .25rem 0;
    color: var(--color-text-muted);
}

.dt-summary-row--total {
    position: relative;
    color: var(--color-text);
    font-weight: 700;
    font-size: 1.25rem;
    margin-top: .75rem;
    padding-top: 1rem;
}

/* A hairline that fades out at both ends, so it sits inside the rounded card
   instead of butting into its corners. Drawn as a pseudo-element because a
   border cannot carry a gradient. */
.dt-summary-row--total::before {
    content: "";
    position: absolute;
    inset-inline: 0;
    top: 0;
    height: 1px;
    background: linear-gradient(to left,
        transparent,
        var(--color-border) 12%,
        var(--color-border) 88%,
        transparent);
}

.dt-summary-row--discount { color: var(--color-success); }

/* ---------- The bar that follows on a phone ---------- */

/* The bar holds a button; it is not a sheet the buyer reads off, so it takes no colour of
   its own. It was drawn as a white panel with a rule and a shadow, which is invisible on a
   white page and a white stripe across the bottom of a page an organiser coloured dark.
   Transparent is also what the desktop width has always done with it -- this makes the two
   agree instead of undoing the paint one breakpoint later.

   What sits on it then reads off the PAGE, so _PublicLayout colours it from the page
   colour, the way it already does the checkout steps. */
.dt-actionbar {
    position: sticky;
    bottom: 0;
    z-index: 5;
    padding: .75rem 1rem calc(.75rem + env(safe-area-inset-bottom));
    margin-inline: -1rem;
}

@media (min-width: 768px) {
    .dt-actionbar {
        position: static;
        margin-inline: 0;
        padding: 0;
    }
}

.dt-actionbar-total { display: flex; justify-content: space-between; align-items: baseline; }

@media (min-width: 768px) { .dt-actionbar-total { display: none; } }

/* Tall enough to hit without aiming, on every control the buyer actually uses. */
.dt-checkout .btn-lg { min-height: 3.25rem; font-size: 1.0625rem; font-weight: 600; }

.dt-checkout .btn-primary {
    background: var(--color-primary);
    border-color: transparent;
    color: var(--color-on-primary);
}

.dt-checkout .btn-primary:hover:not(:disabled) { background: var(--color-primary-dark); }

/* The quantity picker on the checkout, tinted in the event's own colour rather than left
   white: the two controls a buyer actually presses should read as controls at a glance,
   and the number between them stays on the surface so it is the thing that stands out. */
.dt-checkout .dt-qty-pill .dt-qty-btn,
.dt-checkout .dt-qty-circles .dt-qty-btn {
    /* Lighter than the row it sits in, and opaque, so it stays lighter once that row is
       chosen. A tint at the same strength as the row made the buttons vanish into it. */
    background: color-mix(in srgb, var(--color-primary) 4%, var(--color-surface));
    color: var(--color-primary-dark);
    transition: background var(--transition), color var(--transition);
}

.dt-checkout .dt-qty-pill .dt-qty-btn:hover:not(:disabled),
.dt-checkout .dt-qty-circles .dt-qty-btn:hover:not(:disabled) {
    background: var(--color-primary);
    color: var(--color-on-primary);
}

.dt-checkout .dt-qty-btn:disabled { background: var(--color-border); color: var(--color-text-muted); }

.dt-checkout .dt-qty-pill,
.dt-checkout .dt-qty-value { background: var(--color-surface); }

/* The sponsors' strip at the foot of a public page. Scaled to fit rather than cropped —
   a logo cut in half is worse than one shown small, and the organiser agreed the layout
   with whoever is on it. */
.dt-footer-strip {
    padding: .75rem 1rem 0;
    margin-inline: auto;
}

.dt-footer-strip img {
    display: block;
    width: 100%;
    height: auto;
    margin-inline: auto;
}

/* ---------- The opening with the picture blended in ----------
   The picture spans the whole opening and fades into the surface colour where the words
   are. Faded to the surface, not merely dimmed: the title must stay readable over any
   photograph an organiser uploads next week, and a veil tuned to one image is a title
   that disappears when they change it. */

.dt-co-hero--blend .dt-co-hero-grid { grid-template-columns: 1fr; }

.dt-co-hero--blend .dt-co-hero-art {
    position: absolute;
    inset: 0;
    min-height: 0;
    background: none;
}

.dt-co-hero--blend .dt-co-hero-art img { height: 100%; }

.dt-co-hero--blend .dt-co-hero-art::after {
    content: '';
    position: absolute;
    inset: 0;
    /* On a phone the words sit under the picture, so the fade runs downwards. */
    background: linear-gradient(to top,
        var(--color-surface) 0%, var(--color-surface) 52%, transparent 92%);
}

.dt-co-hero--blend .dt-co-hero-body {
    position: relative;
    z-index: 1;
    padding-top: 7rem;
}

@media (min-width: 768px) {
    /* Side by side, so the fade runs towards the words — leftwards in Hebrew. */
    .dt-co-hero--blend .dt-co-hero-grid { min-height: 300px; }

    .dt-co-hero--blend .dt-co-hero-body { padding-top: 2.5rem; max-width: 60%; }

    .dt-co-hero--blend .dt-co-hero-art::after {
        background: linear-gradient(to left,
            var(--color-surface) 0%, var(--color-surface) 38%, transparent 82%);
    }

    [dir="ltr"] .dt-co-hero--blend .dt-co-hero-art::after {
        background: linear-gradient(to right,
            var(--color-surface) 0%, var(--color-surface) 38%, transparent 82%);
    }
}

/* ---------- The company's image gallery ---------- */

.dt-gallery {
    position: fixed;
    inset: 0;
    z-index: 1060;
    background: rgba(32, 35, 43, .45);
    display: grid;
    place-items: center;
    padding: 1rem;
}

.dt-gallery[hidden] { display: none; }

.dt-gallery-sheet {
    background: var(--dt-surface);
    border-radius: var(--dt-radius);
    box-shadow: var(--dt-shadow);
    width: min(900px, 100%);
    max-height: 85vh;
    display: flex;
    flex-direction: column;
}

.dt-gallery-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 1rem;
    border-bottom: 1px solid var(--dt-border);
}

.dt-gallery-body {
    padding: 1rem;
    overflow-y: auto;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: .75rem;
}

.dt-gallery-empty {
    display: block;
    text-align: center;
    color: var(--dt-muted);
    padding: 2.5rem 1rem;
}

.dt-gallery-tile { position: relative; }

.dt-gallery-pick {
    display: block;
    width: 100%;
    padding: 0;
    border: 1px solid var(--dt-border);
    border-radius: var(--dt-radius-sm);
    background: var(--dt-bg);
    overflow: hidden;
    cursor: pointer;
}

.dt-gallery-pick:hover, .dt-gallery-pick:focus-visible { border-color: var(--dt-primary); }

/* Contained, not cropped: the point of the grid is recognising which picture is which. */
.dt-gallery-pick img { width: 100%; height: 110px; object-fit: contain; display: block; }

.dt-gallery-name {
    font-size: .75rem;
    color: var(--dt-muted);
    margin-top: .25rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.dt-gallery-warn { font-size: .7rem; color: var(--dt-warning); }

.dt-gallery-drop {
    position: absolute;
    top: .25rem;
    inset-inline-end: .25rem;
    width: 1.75rem;
    height: 1.75rem;
    border: 0;
    border-radius: 50%;
    background: rgba(32, 35, 43, .65);
    color: #fff;
    font-size: .7rem;
    display: grid;
    place-items: center;
    cursor: pointer;
}

.dt-gallery-drop:hover { background: var(--dt-danger); }


/* ---------- Cancelling an order ---------- */

/* Outline red on a saturated page reads as an error the buyer has already made, not as
   something they may choose to do. A soft pastel says the same thing quietly, and keeps
   its own background so it does not borrow whatever colour the organiser set behind it. */
.dt-btn-cancel {
    --dt-cancel-ink: #9f3a38;
    background: color-mix(in srgb, #e8b4b8 42%, white);
    border: 1px solid color-mix(in srgb, #e8b4b8 70%, white);
    color: var(--dt-cancel-ink);
    font-weight: 600;
}

.dt-btn-cancel:hover,
.dt-btn-cancel:focus-visible {
    background: color-mix(in srgb, #e8b4b8 58%, white);
    border-color: color-mix(in srgb, #e8b4b8 85%, white);
    color: var(--dt-cancel-ink);
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .dt-btn-cancel {
        --dt-cancel-ink: #f3d4d5;
        background: color-mix(in srgb, #8c4a4c 55%, transparent);
        border-color: color-mix(in srgb, #c98b8e 45%, transparent);
    }

    :root:not([data-theme="light"]) .dt-btn-cancel:hover,
    :root:not([data-theme="light"]) .dt-btn-cancel:focus-visible {
        background: color-mix(in srgb, #8c4a4c 72%, transparent);
    }
}

/* ---------- Adding and sending in one press ---------- */

/* Beside a patient outline button, a solid btn-success would read as the thing to press —
   and the thing to press is the one that stops and lets the list be read. A pastel is
   still obviously the go button without shouting over the one that is usually right.
   Its own background, like the cancel button's, so it does not borrow a surface. */
.dt-btn-send {
    --dt-send-ink: #1f6b3a;
    background: color-mix(in srgb, #a8d9b9 42%, white);
    border: 1px solid color-mix(in srgb, #a8d9b9 70%, white);
    color: var(--dt-send-ink);
    font-weight: 600;
}

.dt-btn-send:hover,
.dt-btn-send:focus-visible {
    background: color-mix(in srgb, #a8d9b9 58%, white);
    border-color: color-mix(in srgb, #a8d9b9 85%, white);
    color: var(--dt-send-ink);
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .dt-btn-send {
        --dt-send-ink: #cfecd9;
        background: color-mix(in srgb, #2f6a46 55%, transparent);
        border-color: color-mix(in srgb, #7fb795 45%, transparent);
    }

    :root:not([data-theme="light"]) .dt-btn-send:hover,
    :root:not([data-theme="light"]) .dt-btn-send:focus-visible {
        background: color-mix(in srgb, #2f6a46 72%, transparent);
    }
}

/* ---------- How a shared link will look ---------- */

.dt-share-preview {
    display: flex;
    flex-direction: column;
    max-width: 420px;
    overflow: hidden;
    border: 1px solid var(--color-border);
    border-radius: .5rem;
    background: var(--color-surface, #fff);
}

/* 1.91:1 — the ratio the networks crop to, so what is cut here is what is cut there. */
.dt-share-preview-art {
    aspect-ratio: 1.91 / 1;
    background: var(--color-surface-muted, #f1f3f5) center / cover no-repeat;
    border-bottom: 1px solid var(--color-border);
}

.dt-share-preview-art[hidden] { display: none; }

.dt-share-preview-body { padding: .625rem .75rem; }

.dt-share-preview-host {
    font-size: .75rem;
    text-transform: uppercase;
    color: var(--color-text-muted);
}

.dt-share-preview-title {
    font-weight: 600;
    line-height: 1.3;
    margin-top: .125rem;
}

.dt-share-preview-text {
    font-size: .875rem;
    color: var(--color-text-muted);
    margin-top: .125rem;

    /* Two lines, like the networks themselves. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* ---------- The opening, stacked ---------- */

/* Picture full width above or below the words rather than beside them. One column at
   every width, so the arrangement an organiser picked is the one everybody sees — the
   side-by-side default already collapses to this on a phone by itself. */
.dt-co-hero--stacked .dt-co-hero-grid { grid-template-columns: 1fr; }

@media (min-width: 768px) {
    .dt-co-hero--stacked .dt-co-hero-grid { grid-template-columns: 1fr; }

    /* The default puts the art second on a wide screen; stacked decides its own order. */
    .dt-co-hero--stacked .dt-co-hero-art { order: 0; }
}

.dt-co-hero--above .dt-co-hero-art { order: -1; }

/* A band rather than a column: filling a half-width cell and filling the page are
   different shapes, and a portrait photo across the full width would otherwise push the
   words off the screen. */
.dt-co-hero--stacked .dt-co-hero-art img {
    width: 100%;
    max-height: 420px;
    object-fit: cover;
}

/* ---------- The opening's picture, whole ---------- */

/* contain rather than cover: the whole picture, letterboxed onto whatever is behind it.
   Filling looks deliberate for a photograph — it has no edges that matter — but artwork
   made for an event usually has its title near the top, and a crop eats it. */
.dt-co-hero-art--full img {
    object-fit: contain;
}

/* A fixed height is set inline, and overrides the min-height the layout would otherwise
   impose — otherwise asking for 120px would quietly get 260px on a wide screen. */
.dt-co-hero-art[style*="height"] { min-height: 0; }

/* Natural height when neither a height nor a column constrains it, so a whole picture is
   not squeezed into a band it was never shaped for. */
.dt-co-hero--stacked .dt-co-hero-art--full:not([style*="height"]) img {
    max-height: none;
    height: auto;
}

/* ==========================================================================
   Ticket template: הייטק
   --------------------------------------------------------------------------
   A phone wallet pass rather than a printed stub: the event's poster across the
   top, the code on a card floating over it, the facts on a second card with a
   torn-stub cutout. Only drawn for TicketTemplate.HiTech; Classic is untouched.
   ========================================================================== */

.dt-ht {
    /* Soft and cool rather than the saturated blue of the old page — the card is
       the subject and the background should not compete with it. */
    background: linear-gradient(180deg, #E2EAF4 0%, #F8FAFC 100%) !important;
    background-attachment: fixed;
    color: #111827 !important;
}

/* One column, phone width, centred — on a desktop this is a pass held in a hand,
   not a page stretched across a monitor. */
.dt-ht .dt-wallet-rail,
.dt-ht .dt-wallet-foot,
.dt-ht .dt-wallet-all {
    max-width: var(--dt-ht-width, 430px);
}

.dt-ht .dt-wallet-rail { padding: 0 14px 14px; align-items: flex-start; }
.dt-ht .dt-wallet-slide { justify-content: flex-start; gap: 12px; }

/* On a wide screen the rail wraps and sizes its slides to their content, which left the
   card as wide as its widest line instead of the 430 this template is drawn for. Pinned,
   so the passes stack in a column at the same width a phone shows. */
/* One poster or the other, never both. A phone swipes under a fixed poster; a wide
   screen stacks passes and each needs its own, or the second one starts with a bare
   code and no sign of which event it is for. */
.dt-ht-hero-slide { display: none; }

@media (min-width: 720px) {
    .dt-ht .dt-wallet-slide { flex: 0 0 100%; }

    .dt-ht-hero-page { display: none; }

    .dt-ht-hero-slide {
        display: block;
        width: 100%;
        /* Inside a card stack it is part of the pass, so it is rounded at the top and
           sits flush against the code card below it. */
        border-radius: 18px 18px 0 0;
        margin-bottom: var(--dt-ht-gap, -20px);
    }
}

/* The poster matches the pass, not the window. */
.dt-ht-hero { max-width: var(--dt-ht-width, 430px); }

/* ---- Poster ---- */

.dt-ht-hero {
    position: relative;
    width: 100%;
    max-width: 430px;
    margin-inline: auto;
    /* The organiser's banner height, floored and capped so neither extreme leaves a
       sliver nor pushes the code off the first screen. */
    height: clamp(140px, var(--dt-ht-hero, 28vh), 340px);
    overflow: hidden;
}

.dt-ht-hero-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center center;
    display: block;
}

/* Carries the poster into the page beneath it, so the card below floats on a
   gradient rather than against a hard edge. */
.dt-ht-hero-fade {
    position: absolute;
    inset-inline: 0;
    bottom: 0;
    height: 42%;
    background: linear-gradient(180deg, rgba(226, 234, 244, 0) 0%, #E2EAF4 100%);
    pointer-events: none;
}

/* The overlap is owned by the poster, not by whatever happens to follow it: pulling
   the rail up only worked while the rail was the next element, and it stopped the day
   anything was placed between them. */
.dt-ht-hero { margin-bottom: var(--dt-ht-gap, -20px); }
.dt-ht-hero + * { position: relative; z-index: 1; }

/* ---- Cards ---- */

/* The surface and its ink come from the appearance, like the classic card's — the
   values here are what the template was drawn with, so a card rendered without them
   still looks like itself. Everything inside the card is a transparency of the ink
   rather than a grey of its own, or a dark surface would keep hairlines and labels
   that were chosen against white. */
.dt-ht-card {
    width: 100%;
    background: var(--dt-ht-card-bg, #FFFFFF);
    color: var(--dt-ht-card-ink, #111827);
    border-radius: 26px;
    box-shadow: var(--dt-card-shadow,
                    0 10px 30px rgba(15, 23, 42, .10), 0 2px 6px rgba(15, 23, 42, .05));
    padding: 20px;
}

/* White whatever the event's colours are: a camera reads this, and a tinted or
   inverted code is a guest stuck at the gate. The frame is the code's own panel, so
   the card around it is free to be any colour the organiser likes. */
.dt-ht-qr { text-align: center; }

/* Block with a stated width, not shrink-to-fit: an inline-block frame sizes to the
   image, and an image sized at 100% of that frame resolves to its own intrinsic
   width — so the code came out at whatever the PNG happened to be rather than at
   the width of the card. */
.dt-ht-qr-frame {
    background: #FFFFFF;
    border-radius: 16px;
    padding: 6px;
    display: block;
    /* The organiser's code size, never wider than the card it sits on. */
    width: min(var(--dt-ht-qr, 300px), 100%);
    margin-inline: auto;
}

/* The white panel is the code's size, not the screen's. A code shrunk in the settings
   used to float in the middle of a full-width card, which reads as a layout that has
   not noticed it got smaller. */
.dt-ht-qr {
    width: fit-content;
    max-width: 100%;
    margin-inline: auto;
}

/* As large as the card allows, and square. The code is the whole point of the
   page: it is never shrunk to fit more beneath it — the page scrolls instead. */
.dt-ht-qr-img {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 1 / 1;
    /* Keeps the module edges square if it is ever scaled up at all, rather than
       blurring them into grey, which is what a reader struggles with. */
    image-rendering: pixelated;
}

.dt-ht-qr-note {
    margin: 12px 0 0;
    font-size: 13.5px;
    font-weight: 600;
    color: rgb(from var(--dt-ht-card-ink, #374151) r g b / 85%);
}

/* Where the code would have been, on a ticket that no longer has one. The same red as
   the notice on the wallet page, so the two read as one statement. */
.dt-ht-qr-void {
    color: #b91c1c;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
}

.dt-ht-qr-code {
    margin: 4px 0 0;
    font-size: 13px;
    font-weight: 600;
    color: rgb(from var(--dt-ht-card-ink, #9CA3AF) r g b / 55%);
    letter-spacing: .06em;
}

/* ---- Facts ---- */

.dt-ht-facts { padding: 6px 20px; }

.dt-ht-fact {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 0;
}

/* A hairline between facts, not a band: bands turn the card into a table. */
.dt-ht-fact + .dt-ht-fact {
    border-top: 1px solid rgb(from var(--dt-ht-card-ink, #111827) r g b / 11%);
}

.dt-ht-chip {
    flex: 0 0 42px;
    height: 42px;
    border-radius: 13px;
    display: grid;
    place-items: center;
    font-size: 19px;
}

.dt-ht-fact-body { display: flex; flex-direction: column; min-width: 0; }

.dt-ht-label {
    font-size: 12.5px;
    color: rgb(from var(--dt-ht-card-ink, #6B7280) r g b / 65%);
    line-height: 1.4;
}

.dt-ht-value {
    font-size: 16px;
    font-weight: 700;
    color: var(--dt-ht-card-ink, #111827);
    line-height: 1.4;
    overflow-wrap: anywhere;
}

.dt-ht-value-sm { font-size: 14.5px; font-weight: 600; }

/* ---- Stub cutout ----
   The notches are cut out of the card and show the page through, so they read as
   torn rather than as two circles laid on top of the text. */

.dt-ht-cut {
    position: relative;
    height: 0;
    margin: 6px -20px;
    border-top: 2px dashed rgb(from var(--dt-ht-card-ink, #111827) r g b / 22%);
}

.dt-ht-cut::before,
.dt-ht-cut::after {
    content: "";
    position: absolute;
    top: -14px;
    width: 26px;
    height: 26px;
    border-radius: 999px;
    /* The page's own gradient at this height, so the notch matches what is behind
       the card rather than being a grey disc. */
    background: #EDF2F8;
}

.dt-ht-cut::before { inset-inline-start: -13px; }
.dt-ht-cut::after { inset-inline-end: -13px; }

/* Two to a row, and as many rows as there are facts. Adding the phone and the identity
   number makes it three rows, not a second column squeezed in beside the first. */
.dt-ht-stub {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 12px 14px;
    padding: 16px 0 6px;
}

.dt-ht-stub-item { display: flex; align-items: center; gap: 10px; min-width: 0; }

.dt-ht-stub-icon {
    flex: 0 0 34px;
    height: 34px;
    border-radius: 11px;
    background: rgb(from var(--dt-ht-card-ink, #475569) r g b / 9%);
    color: rgb(from var(--dt-ht-card-ink, #475569) r g b / 75%);
    display: grid;
    place-items: center;
    font-size: 16px;
}

/* A single item should not sit in half a card. */
.dt-ht-stub-item:only-child { grid-column: 1 / -1; }

/* ---- Actions ---- */

/* A row that fills from the leading edge rather than a fixed grid: the actions are
   whichever ones this browser and this event can actually do — two wallets, one, or
   none but share — and columns sized for four leave gaps when there are two. */
.dt-ht-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-start;
    gap: 10px;
    width: 100%;
    margin-top: 2px;
    /* Clear of the home indicator on a phone. */
    padding-bottom: env(safe-area-inset-bottom, 0);
}

.dt-ht-action { flex: 1 1 auto; min-width: 140px; }

.dt-ht-action {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    min-height: 50px;
    padding: 0 14px;
    border: 0;
    border-radius: 15px;
    background: #FFFFFF;
    color: #111827;
    font-size: 14.5px;
    font-weight: 600;
    text-decoration: none;
    box-shadow: 0 2px 8px rgba(15, 23, 42, .08);
}

.dt-ht-action:hover { color: #111827; }
.dt-ht-action .bi { font-size: 17px; }

.dt-ht-action-dark { background: #000000; color: #FFFFFF; }
.dt-ht-action-dark:hover { color: #FFFFFF; }

/* Share is the odd one out — it is not a wallet, so it sits at the far end rather than
   in the run of them. */
.dt-ht-share { flex: 0 0 auto; margin-inline-start: auto; min-width: 0; }

.dt-ht .dt-wallet-foot { padding-top: 4px; }

/* ---------- Pager ----------
   Count on one side, size and arrows on the other. The count comes first because it is
   what an operator actually looks for: how many are there, and am I seeing them all. */
.dt-pager {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .75rem 1rem;
    margin-top: .85rem;
    padding-top: .75rem;
    border-top: 1px solid var(--color-border);
    font-size: .875rem;
}

.dt-pager-count { color: var(--color-text-muted); margin-inline-end: auto; }

.dt-pager-size {
    display: flex;
    align-items: center;
    gap: .5rem;
}

.dt-pager-size .form-select { width: auto; }

.dt-pager-nav { display: flex; align-items: center; gap: .5rem; }

.dt-pager-page { color: var(--color-text-muted); white-space: nowrap; }

/* On a phone the count takes its own line rather than squeezing the controls. */
@media (max-width: 575.98px) {
    .dt-pager-count { flex: 0 0 100%; margin-inline-end: 0; }
}

/* ---------- הייטק inside the appearance editor ----------
   The template is a whole page, so the preview carries the page's own surface rather
   than sitting on the editor's white card — otherwise the organiser is shown something
   the guest never sees. Scoped to .dt-ht-preview so none of it reaches the real page. */
.dt-ht-preview {
    border-radius: 18px;
    overflow: hidden;
    padding-bottom: 14px;
}

.dt-ht-preview .dt-ht-hero { height: 150px; }

/* The organiser has not uploaded artwork yet: say which band it will fill rather than
   leaving a gap that reads as a broken image. */
.dt-ht-preview-poster {
    width: 100%;
    height: 100%;
    display: grid;
    place-items: center;
    background: linear-gradient(135deg, #CBD5E1, #94A3B8);
    color: #475569;
    font-size: 13px;
    font-weight: 600;
}

.dt-ht-preview-body { padding: 0 12px; display: flex; flex-direction: column; gap: 10px; }

/* The real page caps the code at the organiser's size; in a narrow editor column it is
   the column that decides. */
.dt-ht-preview .dt-ht-qr-frame { width: min(var(--dt-ht-qr, 300px), 100%); }

/* A bar for one comparison — the busiest day against the rest. A chart library for this
   would be a dependency to draw a rectangle. */
.dt-report-bar {
    background: rgb(from var(--color-text-muted) r g b / 15%);
    border-radius: 999px;
    height: 8px;
    overflow: hidden;
}

.dt-report-bar span {
    display: block;
    height: 100%;
    background: var(--color-primary);
    border-radius: 999px;
}

/* Who this page is showing an entitlement for.
   ────────────────────────────────────────────
   A chip with its own surface, not a line of text on the page's. The event's colours are
   the organiser's, and on a dark one this read as dark text on dark: the single thing
   that says whose session a shared counter is in was the thing nobody could see.

   The surface comes from .dt-panel, which the public layout paints with the colour the
   organiser picked under עיצוב and a text colour worked out from it. Deriving it from
   currentColor instead looked right on paper and came out invisible on a dark blue page
   — and it also meant this one chip answered to nothing in the appearance editor. */
.dt-identified {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
    margin-bottom: .75rem;
    padding: .5rem .75rem;
    border-radius: 10px;

    /* The fallback, for an event whose organiser has set no panel colour — the layout
       emits the .dt-panel rule only when there is one, and without it the chip would
       have no surface at all. Where there IS one, that rule is written into the page
       after this stylesheet and wins on order, which is what puts the organiser's own
       colour on this bar. */
    background: rgb(from currentColor r g b / 10%);
    border: 1px solid rgb(from currentColor r g b / 22%);
}

.dt-identified .bi-person-check { font-size: 1.1rem; flex: none; opacity: .8; }

/* A link that is still legible when the surface is dark: it inherits the colour the
   text around it already proved readable, and says it is a control by its underline. */
.dt-identified-out {
    border: 0;
    background: none;
    padding: 0;
    font: inherit;
    font-size: .875rem;
    color: inherit;
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
    opacity: .85;
}

.dt-identified-out:hover { opacity: 1; }

/* The save row, kept within reach.
   ───────────────────────────────
   The long screens here run to a thousand lines of markup — עיצוב ומיתוג, הגדרות החברה,
   נראות כרטיס — and the only way to save was to scroll to the end of all of it.

   The same row and not a second button somewhere else. Two save buttons are two things
   that can disagree about which form they belong to, which is the kind of defect this
   codebase keeps producing; this is the SAME row, resting where it always did and riding
   the bottom of the screen until you reach it. */
.dt-form-actions {
    display: flex;
    gap: .5rem;
    align-items: center;
    flex-wrap: wrap;
    justify-content: flex-end;  /* the far end of the line: the LEFT, in Hebrew */
    padding-block: .75rem;
}

/* The buttons stay together at that end, with the save on the outside of the pair: it is
   the one being reached for, so it is the one the edge of the screen stops your hand at.
   The way out sits behind it.

   An <a> in this row is that way out — ביטול, חזרה לאירוע. Ordering it ahead of the
   button along the line puts it behind the button on the screen, because the line runs
   right to left. */
.dt-form-actions > a { order: -2; }

/* Riding the bottom of the screen it is a BAR ACROSS THE CONTENT AREA, not a row the
   width of whichever column or card it happens to sit in — in the appearance editor that
   left it ending in mid-air beside the preview panel, with bare page to its left.

   Fixed and not sticky: a sticky box cannot be wider than the container it sticks inside.
   Leaving the flow is what dt-form-actions.js holds a spacer open for, so nothing below
   the row jumps when it lifts off. With the script blocked the row never gets this class
   and stays exactly where it has always been, at the end of the form. */
.dt-form-actions.is-stuck {
    position: fixed;
    inset-inline-start: var(--dt-sidebar-w);     /* clear of the sidebar, not under it */
    inset-inline-end: 0;
    bottom: 0;
    z-index: 1020;              /* under the sidebar at 1040 and the bottom nav at 1035 */
    margin: 0;                  /* a margin here is a gap under a bar at bottom: 0 */
    padding-inline: 10px;
    background: var(--dt-surface);
    border-top: 1px solid var(--dt-border);
    box-shadow: 0 -8px 20px -12px rgba(15, 23, 42, .25);
}

@media (min-width: 992px) {
    .dt-shell-collapsed .dt-form-actions.is-stuck {
        inset-inline-start: var(--dt-sidebar-w-collapsed);
    }
}

@media (max-width: 991.98px) {
    /* No sidebar beside the content, and above the bottom navigation — which is 3.5rem
       plus whatever the phone reserves below it — rather than behind it. */
    .dt-form-actions.is-stuck {
        inset-inline-start: 0;
        bottom: calc(3.5rem + env(safe-area-inset-bottom));
    }
}

/* Said quietly, over on the near side where the eye starts. A banner for this would be a
   banner on every screen somebody has typed one character into.

   order moves it visually only. It is a span: nothing focusable changes place, so the tab
   order is still the document's. */
.dt-form-actions .dt-unsaved {
    order: -3;                  /* ahead of both: the near end, away from the pair */
    margin-inline-end: auto;
    font-size: .8125rem;
    color: var(--dt-warning);
    display: none;
}

.dt-form-actions.is-dirty .dt-unsaved { display: inline; }

/* ---------- A ROW OF ICON BUTTONS THAT POST ----------
   Delete, move, show/hide: each is a form, because each is a POST, and a form in a button
   group brings hidden fields with it — the antiforgery token is appended AFTER the button.
   Bootstrap decides a button's corners from its position among its siblings, so a button
   with a token behind it is read as neither first nor last and BOTH of its ends are
   flattened. The end of the row finished square where it should have been round.

   And a hairline between them instead of a shared border. At icon size two touching
   buttons read as one control, and one of them deletes.

   :has, so this reaches only the rows that post. A segmented control with no form in it —
   a set of tabs, a channel picker — is left joined, which is what it is for. Where :has is
   not understood the row keeps the shared border it has today and nothing is broken.

   The selectors are heavier than they look because Bootstrap's own are: its spacing and
   corner rules carry four classes each, so taking one back needs more. */
.btn-group:has(> form.btn-group) { gap: 1px; }

.btn-group:has(> form.btn-group) > .btn,
.btn-group:has(> form.btn-group) > form.btn-group,
.btn-group:has(> form.btn-group) > form.btn-group > .btn {
    margin-inline: 0;
}

/* The two ends of the row, given back to the buttons that are actually drawn there. */
.btn-group > form.btn-group:first-child > .btn:not(.dropdown-toggle) {
    border-start-start-radius: var(--dt-radius-sm);
    border-end-start-radius: var(--dt-radius-sm);
}

.btn-group > form.btn-group:last-child > .btn:not(.dropdown-toggle) {
    border-start-end-radius: var(--dt-radius-sm);
    border-end-end-radius: var(--dt-radius-sm);
}

/* ---------- A FIELD YOU CAN DROP A FILE ON ----------
   The row that already holds an image's address and its upload button. Picking a file up
   anywhere over the page outlines every one of them — which is how anybody finds out this
   works at all, without a line of text sitting under each field forever — and the one
   under the cursor says what letting go will do.

   The sheet over the row is a pseudo-element and takes no pointer events, or it would
   swallow the drop it is advertising. */
.dt-drop.is-droppable {
    outline: 1px dashed var(--dt-border);
    outline-offset: 3px;
    border-radius: var(--dt-radius-sm);
}

.dt-drop.is-dropping::after {
    content: attr(data-drop-label);
    position: absolute;
    inset: 0;
    z-index: 6;                 /* over a focused field, which Bootstrap lifts to 5 */
    display: flex;
    align-items: center;
    justify-content: center;
    /* Opaque, over the surface colour: the soft tint alone is a tenth of an alpha and
       the address underneath read straight through the sentence covering it. */
    background: linear-gradient(var(--dt-primary-soft), var(--dt-primary-soft)),
                var(--dt-surface);
    border: 1px dashed var(--dt-primary);
    border-radius: var(--dt-radius-sm);
    color: var(--dt-primary);
    font-size: .8125rem;
    font-weight: 600;
    pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
    .dt-drop { transition: outline-color .12s ease; }
}

/* ---------- WHAT A SAVED LOOK LOOKS LIKE ----------
   Three rectangles: the page, a sheet on it, and a button. Not an iframe of the real
   event page — that would load one public page per template on a screen listing twenty of
   them — and not six hex codes, which nobody can picture. */
.dt-template-swatch {
    position: relative;
    display: block;
    height: 5.5rem;
    border: 1px solid var(--dt-border);
    border-radius: var(--dt-radius-sm);
    overflow: hidden;
}

.dt-template-sheet {
    position: absolute;
    inset: .75rem .75rem 0;
    border-radius: var(--dt-radius-sm) var(--dt-radius-sm) 0 0;
    box-shadow: 0 1px 4px rgba(15, 23, 42, .12);
}

.dt-template-button {
    position: absolute;
    inset-block-end: 1rem;
    inset-inline: 1.75rem;
    height: 1.1rem;
    border-radius: 999px;
}
