/* ==========================================================================
   ADMIN

   Bootstrap 5.3 is the base. This file does two things and nothing else:

     1. BRIDGE — hands Bootstrap a different set of values through its own CSS
        variables. No component is redrawn; Bootstrap keeps its layout, its
        states and its accessibility work, and only reads different numbers.

     2. SHELL — the handful of things Bootstrap has no component for: the
        sidebar-and-panel frame, the stat tiles, the media grid, the sign-in
        split.

   What is loaded, in order, by Views/Shared/_Head.cshtml:

     theme/fonts/fonts.css     Cairo and Inter, self-hosted, subset-split
     bootstrap[.rtl].min.css   the base — the correct build for the request's
                               reading direction, picked per request
     theme/css/tokens.css      the brand ramp, the type scale, spacing, motion
     theme/css/icons.css       the icon sprite and its mask classes
     theme/css/bidi.css        icon mirroring — the residue logical properties
                               cannot express
     admin.css                 this file
     admin-libs.css            skins for the four plugins Bootstrap does not
                               cover

   The theme's *components* (its own .btn, .card, .table, .badge) are
   deliberately not loaded. They collide with Bootstrap's classes of the same
   name, and the point of this pass is that Bootstrap draws the components.

   ---------------------------------------------------------------------------
   WHY THIS FILE OWNS A NEUTRAL RAMP

   Wasl.Theme is a *storefront* theme. Its neutrals are tuned for a shopping
   page — a mid-grey #d7d7d7 rule, a flat #f7f7f7 plate, generous 28px leading
   — and a console is not that. Twelve hours of an operator's day are spent in
   this UI reading dense tables, so the chrome has to recede: hairlines rather
   than rules, one weight of surface rather than three, and colour reserved for
   the two or three things on a screen that actually mean something.

   So the brand comes from the theme and the neutrals are declared here, under
   `--ad-*`. Nothing below invents a brand colour, and nothing in the theme has
   to change to keep the storefront looking like a storefront.

   Two rules hold in this file exactly as they do in the theme:

     1. Read tokens, never literals — with two documented exceptions: the
        `--ad-*` ramp itself, which is where the literals live, and the
        comma-separated RGB triplets Bootstrap's legacy rgba() needs.
     2. Logical properties only. No `left`, no `right`, no `margin-left`. The
        admin renders `ar` right-to-left and `en` left-to-right off this one
        file, and a physical side would pin an edge to the wrong one.

   Contents
     1. Neutral ramp and elevation      8.  Cards
     2. Bootstrap bridge                9.  Tables
     3. Typography                      10. Badges and chips
     4. Buttons                         11. Shell: frame, sidebar, topbar
     5. Forms                           12. Stat tiles and cells
     6. Navigation and pagination       13. Media
     7. Overlays                        14. Sign-in, notices, validation
                                        15. Reporting
   ========================================================================== */


/* ==========================================================================
   1. NEUTRAL RAMP AND ELEVATION

   Nine values carry the whole chrome. They are literals on purpose: this is
   the one place in the admin allowed to name a colour, and everything else
   reads these.

   The ramp is very slightly cool (a blue-grey cast, hue ~220) rather than a
   pure grey. Against the theme's green — a warm-leaning hue — a neutral grey
   reads faintly dirty next to it, and a cool one reads clean.
   ========================================================================== */
:root,
[data-bs-theme="light"] {
  --ad-ground:   #f6f7f9;  /* the app ground the panel sits on */
  --ad-surface:  #ffffff;  /* cards, topbar, menus */
  --ad-rail:     #fbfbfc;  /* the sidebar — a half-step off surface */
  --ad-sunken:   #f1f3f5;  /* input plates, hover fills, empty frames */

  --ad-line:     #e7e9ee;  /* hairline: card edges, table rules, dividers */
  --ad-line-strong: #d5d9e0;  /* input borders — must survive being looked at */

  --ad-title:    #14161b;  /* headings, table titles, numbers */
  --ad-text:     #454a54;  /* body copy */
  --ad-muted:    #6b7280;  /* labels, secondary cells */
  --ad-faint:    #9096a1;  /* column heads, slugs, timestamps */

  /* Elevation. Short, tight and nearly colourless — a console is a flat
     surface with a few things lifted off it, not a stack of floating panels.
     The old theme shadows (0 30px 30px) belong on a marketing page. */
  --ad-shadow-xs: 0 1px 2px rgb(20 22 27 / 5%);
  --ad-shadow-sm: 0 1px 3px rgb(20 22 27 / 7%), 0 1px 2px -1px rgb(20 22 27 / 6%);
  --ad-shadow-md: 0 4px 12px -2px rgb(20 22 27 / 10%), 0 2px 6px -3px rgb(20 22 27 / 8%);
  --ad-shadow-lg: 0 16px 36px -10px rgb(20 22 27 / 20%), 0 4px 10px -4px rgb(20 22 27 / 10%);

  /* Geometry. One radius for controls, one for containers — a console with
     five different corner sizes reads as five different pieces of software. */
  /* Label colour on a brand fill. White in light; in dark the brand is the
     theme's light green and white on it fails contrast, so the ground steps in.
     Every filled-green thing reads this rather than hard-coding #fff. */
  --ad-on-primary: #ffffff;

  --ad-radius:    8px;
  --ad-radius-sm: 6px;
  --ad-radius-lg: 10px;

  --ad-sidebar-w: 252px;
  --ad-topbar-h:  58px;
}

/* Dark is the same nine roles at the other end of the ramp, not a filter over
   the light one. Surfaces step *up* from the ground here, which is the
   inversion that makes a dark UI read as layered rather than washed out. */
[data-bs-theme="dark"] {
  --ad-ground:   #0e1014;
  --ad-surface:  #16181e;
  --ad-rail:     #121419;
  --ad-sunken:   #1c1f26;

  --ad-line:     #24272f;
  --ad-line-strong: #333843;

  --ad-title:    #e9ebef;
  --ad-text:     #aeb4be;
  --ad-muted:    #838a95;
  --ad-faint:    #6a707b;

  --ad-on-primary: #0e1014;

  /* Dark surfaces do not catch light, so the shadow has to do more of the
     separating work and gets a harder alpha to do it with. */
  --ad-shadow-xs: 0 1px 2px rgb(0 0 0 / 40%);
  --ad-shadow-sm: 0 1px 3px rgb(0 0 0 / 50%), 0 1px 2px -1px rgb(0 0 0 / 40%);
  --ad-shadow-md: 0 4px 12px -2px rgb(0 0 0 / 55%), 0 2px 6px -3px rgb(0 0 0 / 45%);
  --ad-shadow-lg: 0 16px 36px -10px rgb(0 0 0 / 65%), 0 4px 10px -4px rgb(0 0 0 / 50%);
}


/* ==========================================================================
   2. BOOTSTRAP BRIDGE

   Everything Bootstrap paints reads from here. Setting the variable rather
   than writing the rule is what keeps this file short and what keeps
   Bootstrap's own states — :hover, :disabled, .active, validation — working
   without being re-implemented.
   ========================================================================== */
:root,
[data-bs-theme="light"],
[data-bs-theme="dark"] {
  /* ---- Palette --------------------------------------------------------
     Brand from the theme; neutrals from the ramp above. */
  --bs-primary:   var(--color-primary);
  --bs-secondary: var(--color-secondary);
  --bs-success:   var(--color-success);
  --bs-light:     var(--ad-sunken);
  --bs-dark:      var(--ad-title);

  /* These three read their own triplet rather than the theme token, and that is
     deliberate: the triplets below are re-tuned for a console (the storefront's
     pure #e00000 and its #f8c519 both fail contrast on white at badge size).
     Pointing `--bs-danger` at the token and `--bs-danger-rgb` at the tuned
     value would give `.btn-danger` one red and `.text-danger` another. */
  --bs-info:      rgb(var(--bs-info-rgb));
  --bs-warning:   rgb(var(--bs-warning-rgb));
  --bs-danger:    rgb(var(--bs-danger-rgb));

  /* ---- Type -----------------------------------------------------------
     14px, not Bootstrap's 16. A console is read in rows, not paragraphs:
     16px costs about two table rows a screen and buys nothing back, and
     every admin UI worth copying — Stripe, Linear, GitHub — sits at 14. */
  --bs-font-sans-serif:   var(--font-sans);
  --bs-font-monospace:    var(--font-mono);
  --bs-body-font-family:  var(--font-body);
  --bs-body-font-size:    0.875rem;
  --bs-body-line-height:  1.55;

  /* ---- Surfaces and ink ---------------------------------------------- */
  --bs-body-color:      var(--ad-text);
  --bs-body-bg:         var(--ad-surface);
  --bs-emphasis-color:  var(--ad-title);
  --bs-heading-color:   var(--ad-title);
  --bs-secondary-color: var(--ad-muted);
  --bs-tertiary-color:  var(--ad-faint);
  --bs-secondary-bg:    var(--ad-sunken);
  --bs-tertiary-bg:     var(--ad-sunken);
  --bs-border-color:    var(--ad-line);

  --bs-link-color:       var(--color-primary);
  --bs-link-hover-color: var(--color-primary-hover);

  /* ---- Geometry and elevation ---------------------------------------- */
  --bs-border-radius:      var(--ad-radius);
  --bs-border-radius-sm:   var(--ad-radius-sm);
  --bs-border-radius-lg:   var(--ad-radius-lg);
  --bs-border-radius-xl:   12px;
  --bs-border-radius-pill: var(--radius-pill);

  --bs-box-shadow:    var(--ad-shadow-md);
  --bs-box-shadow-sm: var(--ad-shadow-sm);
  --bs-box-shadow-lg: var(--ad-shadow-lg);

  /* One focus ring for the whole console, on every control, always visible.
     3px at 18% is wide enough to find on a dense screen and quiet enough to
     live with. */
  --bs-focus-ring-color: rgb(var(--rgb-primary) / 18%);
  --bs-focus-ring-width: 3px;
}

/*
   Bootstrap's contextual classes still use the legacy `rgba(var(--x-rgb), a)`
   form, which needs comma-separated channels and cannot read a modern
   `R G B` triplet. These six are the documented exception to "tokens, never
   literals" — they are the same colours the theme names, in the only notation
   that works here. Change one and change its token in tokens.css with it.
*/
:root,
[data-bs-theme="light"] {
  --bs-primary-rgb:   34, 82, 77;     /* --green-600 (Zilly deep green/teal) */
  --bs-success-rgb:   27, 128, 87;    /* --color-success — a status green, no longer the brand */
  --bs-secondary-rgb: 27, 66, 62;     /* --green-700  */
  --bs-danger-rgb:    200, 38, 38;    /* softened from the storefront's pure red */
  --bs-warning-rgb:   180, 123, 0;    /* readable amber — the token yellow fails on white */
  --bs-info-rgb:      37, 99, 235;
}

[data-bs-theme="dark"] {
  --bs-primary-rgb:   79, 145, 138;   /* --green-400  */
  --bs-success-rgb:   71, 185, 138;   /* --color-success, lifted for a dark ground */
  --bs-secondary-rgb: 134, 181, 175;  /* --green-300  */
  --bs-danger-rgb:    248, 113, 113;
  --bs-warning-rgb:   234, 179, 8;
  --bs-info-rgb:      96, 145, 255;
}

/*
   The subtle pairs. Bootstrap 5.3's own recommendation for a quiet badge or a
   quiet alert is `text-*-emphasis` over `bg-*-subtle` inside `border-*-subtle`,
   and StatusView.BadgeClass emits exactly that trio — so these three values
   per colour are what every state badge on every screen is actually made of.

   Kept low: 10% fill and 22% border. A status column is six badges deep, and
   at storefront saturation that column becomes the loudest thing on a page
   whose subject is the offer titles beside it.
*/
:root,
[data-bs-theme="light"],
[data-bs-theme="dark"] {
  --bs-primary-bg-subtle:     rgba(var(--bs-primary-rgb), 0.1);
  --bs-primary-border-subtle: rgba(var(--bs-primary-rgb), 0.22);
  --bs-success-bg-subtle:     rgba(var(--bs-success-rgb), 0.1);
  --bs-success-border-subtle: rgba(var(--bs-success-rgb), 0.22);
  --bs-warning-bg-subtle:     rgba(var(--bs-warning-rgb), 0.12);
  --bs-warning-border-subtle: rgba(var(--bs-warning-rgb), 0.26);
  --bs-danger-bg-subtle:      rgba(var(--bs-danger-rgb), 0.1);
  --bs-danger-border-subtle:  rgba(var(--bs-danger-rgb), 0.22);
  --bs-info-bg-subtle:        rgba(var(--bs-info-rgb), 0.1);
  --bs-info-border-subtle:    rgba(var(--bs-info-rgb), 0.22);
  --bs-secondary-bg-subtle:   var(--ad-sunken);
  --bs-secondary-border-subtle: var(--ad-line);

  --bs-form-invalid-color:        rgb(var(--bs-danger-rgb));
  --bs-form-invalid-border-color: rgb(var(--bs-danger-rgb));
}

/* The text half of those pairs. In light it has to be dark enough to read on
   a 10% tint, so it is not the fill colour; in dark the fill is dark and the
   text can be the colour itself. */
:root,
[data-bs-theme="light"] {
  --bs-primary-text-emphasis: var(--color-primary-hover);
  --bs-success-text-emphasis: var(--color-primary-hover);
  --bs-warning-text-emphasis: #8a5d00;
  --bs-danger-text-emphasis:  #a01c1c;
  --bs-info-text-emphasis:    #1d4ed8;
}

[data-bs-theme="dark"] {
  --bs-primary-text-emphasis: rgb(var(--bs-primary-rgb));
  --bs-success-text-emphasis: rgb(var(--bs-success-rgb));
  --bs-warning-text-emphasis: rgb(var(--bs-warning-rgb));
  --bs-danger-text-emphasis:  rgb(var(--bs-danger-rgb));
  --bs-info-text-emphasis:    rgb(var(--bs-info-rgb));
}


/* ==========================================================================
   3. TYPOGRAPHY

   The theme's scale is a marketing scale — 44px headlines, 28px leading. A
   console needs four sizes and one of them is 14px. These override the scale
   rather than the family: `--font-sans` still points at Cairo, which is what
   makes both scripts one texture.
   ========================================================================== */
body {
  /* Both are needed together: Inter and Cairo ship the alternates, and without
     the hint the Latin looks like a webfont from 2014. */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-feature-settings: "cv02", "cv03", "cv04", "cv11";
}

h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
  font-weight: var(--fw-semibold);
  letter-spacing: -0.011em;   /* optical correction, not a style — large text
                                 at default tracking reads loose */
  line-height: 1.3;
}

h1, .h1 { font-size: 1.5rem; }
h2, .h2 { font-size: 1.25rem; }
h3, .h3 { font-size: 1.125rem; }
h4, .h4 { font-size: 1rem; }
h5, .h5 { font-size: 0.9375rem; }
h6, .h6 { font-size: 0.875rem; }

/* Every number that sits in a column: dates, counts, prices, percentages.
   Proportional digits make a column of figures ragged, which is the one thing
   a column of figures must not be. */
.admin__stat-value,
.admin__cell-dates,
.table td,
.table th { font-variant-numeric: tabular-nums; }

/* The one-line explanation under a page title. */
.admin__lead {
  max-width: 68ch;
  color: var(--ad-muted);
}

/* Turns an icon back against the reading direction — see the comment where the
   views use it. */
.admin__icon-reverse { transform: scaleX(calc(-1 * var(--flip))); }


/* ==========================================================================
   4. BUTTONS
   ========================================================================== */
.btn {
  --bs-btn-font-weight: var(--fw-medium);
  --bs-btn-border-radius: var(--ad-radius);
  --bs-btn-padding-y: 0.4375rem;
  --bs-btn-padding-x: 0.8125rem;
  --bs-btn-font-size: 0.875rem;

  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  white-space: nowrap;
  transition: background-color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease),
              box-shadow var(--dur-fast) var(--ease);
}

.btn-sm {
  --bs-btn-padding-y: 0.3125rem;
  --bs-btn-padding-x: 0.625rem;
  --bs-btn-font-size: 0.8125rem;
  --bs-btn-border-radius: var(--ad-radius-sm);
}

.btn-lg {
  --bs-btn-padding-y: 0.5625rem;
  --bs-btn-padding-x: 1rem;
  --bs-btn-font-size: 0.9375rem;
  --bs-btn-border-radius: var(--ad-radius);
}

.btn-primary {
  --bs-btn-bg: var(--color-primary);
  --bs-btn-border-color: var(--color-primary);
  --bs-btn-hover-bg: var(--color-primary-hover);
  --bs-btn-hover-border-color: var(--color-primary-hover);
  --bs-btn-active-bg: var(--color-primary-hover);
  --bs-btn-active-border-color: var(--color-primary-hover);
  --bs-btn-disabled-bg: var(--color-primary);
  --bs-btn-disabled-border-color: var(--color-primary);
  --bs-btn-focus-shadow-rgb: var(--bs-primary-rgb);
  box-shadow: var(--ad-shadow-xs);
}

/* In dark the primary is the light green from the theme's dark ramp, and white
   label text on it fails contrast. The ground colour is the only thing dark
   enough to sit on it. */
[data-bs-theme="dark"] .btn-primary {
  --bs-btn-color: var(--ad-on-primary);
  --bs-btn-hover-color: var(--ad-on-primary);
  --bs-btn-active-color: var(--ad-on-primary);
  --bs-btn-disabled-color: var(--ad-on-primary);
  box-shadow: none;
}

/* The default secondary action, and the most-used button in the console: every
   row's Edit, every Cancel, every icon button in the topbar. A hairline box
   that fills on hover. */
.btn-outline-secondary {
  --bs-btn-color: var(--ad-title);
  --bs-btn-bg: var(--ad-surface);
  --bs-btn-border-color: var(--ad-line-strong);
  --bs-btn-hover-color: var(--ad-title);
  --bs-btn-hover-bg: var(--ad-sunken);
  --bs-btn-hover-border-color: var(--ad-line-strong);
  --bs-btn-active-color: var(--ad-title);
  --bs-btn-active-bg: var(--ad-sunken);
  --bs-btn-active-border-color: var(--ad-line-strong);
  --bs-btn-disabled-color: var(--ad-faint);
  --bs-btn-disabled-border-color: var(--ad-line);
  --bs-btn-focus-shadow-rgb: var(--bs-primary-rgb);
}

.btn-outline-primary {
  --bs-btn-color: var(--color-primary);
  --bs-btn-border-color: rgba(var(--bs-primary-rgb), 0.4);
  --bs-btn-hover-bg: var(--color-primary);
  --bs-btn-hover-border-color: var(--color-primary);
  --bs-btn-active-bg: var(--color-primary-hover);
  --bs-btn-active-border-color: var(--color-primary-hover);
  --bs-btn-disabled-color: var(--color-primary);
  --bs-btn-disabled-border-color: var(--ad-line);
  --bs-btn-focus-shadow-rgb: var(--bs-primary-rgb);
}

.btn-secondary {
  --bs-btn-bg: var(--color-secondary);
  --bs-btn-border-color: var(--color-secondary);
  --bs-btn-hover-bg: var(--ad-title);
  --bs-btn-hover-border-color: var(--ad-title);
  --bs-btn-active-bg: var(--ad-title);
  --bs-btn-active-border-color: var(--ad-title);
}

.btn-danger,
.btn-outline-danger { --bs-btn-focus-shadow-rgb: var(--bs-danger-rgb); }

.btn-link {
  --bs-btn-color: var(--color-primary);
  --bs-btn-hover-color: var(--color-primary-hover);
  --bs-btn-font-weight: var(--fw-medium);
  text-decoration: none;
}

/* The trailing arrow on a "go to the full list" link leans in the reading
   direction on hover. `--flip` is what makes that the correct direction in
   Arabic without a second rule. */
.btn-link .icon { transition: transform var(--dur) var(--ease); }
.btn-link:hover .icon { transform: translateX(calc(3px * var(--flip))); }

/* A square button holding one glyph. */
.btn-icon {
  --bs-btn-padding-x: 0;
  --bs-btn-padding-y: 0;
  width: 34px;
  height: 34px;
  flex: 0 0 auto;
}

.btn-icon.btn-sm { width: 30px; height: 30px; }


/* ==========================================================================
   5. FORMS

   Controls are 34px tall to match .btn, so a field and the button beside it
   line up without either being nudged.
   ========================================================================== */
.form-label {
  margin-block-end: 0.375rem;
  font-size: 0.8125rem;
  font-weight: var(--fw-medium);
  color: var(--ad-title);
}

.form-label .req {
  margin-inline-start: 2px;
  color: rgb(var(--bs-danger-rgb));
}

.form-control,
.form-select {
  --bs-border-radius: var(--ad-radius);
  border-color: var(--ad-line-strong);
  padding-block: 0.4375rem;
  padding-inline: 0.6875rem;
  font-size: 0.875rem;
  background-color: var(--ad-surface);
  transition: border-color var(--dur-fast) var(--ease),
              box-shadow var(--dur-fast) var(--ease);
}

.form-control::placeholder { color: var(--ad-faint); }

.form-control:focus,
.form-select:focus {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 var(--bs-focus-ring-width) var(--bs-focus-ring-color);
}

.form-control:disabled,
.form-select:disabled { background-color: var(--ad-sunken); color: var(--ad-muted); }

/* The affix on an input-group — a search glyph, a `%`, a currency. It is a
   label for the field, not a control, so it sits on the sunken plate. */
.input-group-text {
  border-color: var(--ad-line-strong);
  background-color: var(--ad-sunken);
  color: var(--ad-muted);
  padding-inline: 0.6875rem;
}

.form-check-input { border-color: var(--ad-line-strong); }
.form-check-input:checked {
  background-color: var(--color-primary);
  border-color: var(--color-primary);
}
.form-check-input:focus {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 var(--bs-focus-ring-width) var(--bs-focus-ring-color);
}

.form-text {
  margin-block-start: 0.375rem;
  font-size: 0.75rem;
  color: var(--ad-muted);
}


/* ==========================================================================
   6. NAVIGATION AND PAGINATION
   ========================================================================== */
.nav-pills {
  --bs-nav-pills-border-radius: var(--ad-radius);
  --bs-nav-pills-link-active-bg: var(--color-primary);
  --bs-nav-link-font-weight: var(--fw-medium);
}

/* Underline tabs, not folder tabs. The 2px rule under the active tab is the
   whole indicator — Bootstrap's default draws three borders and a notch, which
   is a lot of geometry to say "this one". */
.nav-tabs {
  --bs-nav-tabs-border-color: var(--ad-line);
  --bs-nav-link-color: var(--ad-muted);
  --bs-nav-link-hover-color: var(--ad-title);
  --bs-nav-link-font-weight: var(--fw-medium);
  --bs-nav-link-padding-y: 0.5rem;
  gap: var(--space-1);
}

.nav-tabs .nav-link {
  border: 0;
  border-block-end: 2px solid transparent;
  border-radius: 0;
  margin-block-end: -1px;
}

.nav-tabs .nav-link:hover { border-block-end-color: var(--ad-line-strong); }

.nav-tabs .nav-link.active {
  background: none;
  color: var(--color-primary);
  border-block-end-color: var(--color-primary);
}

.pagination {
  --bs-pagination-color: var(--ad-text);
  --bs-pagination-bg: var(--ad-surface);
  --bs-pagination-border-color: var(--ad-line);
  --bs-pagination-border-radius: var(--ad-radius-sm);
  --bs-pagination-padding-x: 0.625rem;
  --bs-pagination-hover-color: var(--ad-title);
  --bs-pagination-hover-bg: var(--ad-sunken);
  --bs-pagination-hover-border-color: var(--ad-line);
  --bs-pagination-focus-color: var(--ad-title);
  --bs-pagination-focus-bg: var(--ad-sunken);
  --bs-pagination-focus-box-shadow: 0 0 0 var(--bs-focus-ring-width) var(--bs-focus-ring-color);
  --bs-pagination-active-bg: var(--color-primary);
  --bs-pagination-active-border-color: var(--color-primary);
  --bs-pagination-disabled-color: var(--ad-faint);
  --bs-pagination-disabled-bg: var(--ad-surface);
  --bs-pagination-disabled-border-color: var(--ad-line);
  gap: var(--space-1);
}

/* Gapped, so each page control is its own box rather than a segment of one
   long bar. Bootstrap's collapsed corners assume the joined form. */
.pagination .page-link { border-radius: var(--ad-radius-sm); }

[data-bs-theme="dark"] .pagination { --bs-pagination-active-color: var(--ad-ground); }


/* ==========================================================================
   7. OVERLAYS — dropdowns, toasts, modals, tooltips, alerts
   ========================================================================== */
.dropdown-menu {
  --bs-dropdown-border-color: var(--ad-line);
  --bs-dropdown-border-radius: var(--ad-radius-lg);
  --bs-dropdown-bg: var(--ad-surface);
  --bs-dropdown-link-color: var(--ad-text);
  --bs-dropdown-link-hover-bg: var(--ad-sunken);
  --bs-dropdown-link-hover-color: var(--ad-title);
  --bs-dropdown-link-active-bg: var(--color-primary);
  --bs-dropdown-padding-y: 0.3125rem;
  --bs-dropdown-item-padding-y: 0.4375rem;
  --bs-dropdown-item-padding-x: 0.75rem;
  --bs-dropdown-item-border-radius: var(--ad-radius-sm);
  --bs-dropdown-font-size: 0.875rem;
  box-shadow: var(--ad-shadow-md);
  padding-inline: 0.3125rem;
}

/* Bootstrap has no per-item radius knob; the rounded hover is what makes a
   menu read as a list of things rather than a stack of bands. */
.dropdown-item { border-radius: var(--ad-radius-sm); }

.dropdown-header {
  font-size: 0.6875rem;
  font-weight: var(--fw-semibold);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ad-faint);
}

.dropdown-menu form { margin: 0; }
.dropdown-menu form .dropdown-item { width: 100%; text-align: start; }

.dropdown-item-danger { color: rgb(var(--bs-danger-rgb)); }
.dropdown-item-danger:hover,
.dropdown-item-danger:focus {
  background-color: var(--bs-danger-bg-subtle);
  color: var(--bs-danger-text-emphasis);
}

.dropdown-divider { border-color: var(--ad-line); }

.toast {
  --bs-toast-border-color: var(--ad-line);
  --bs-toast-border-radius: var(--ad-radius-lg);
  --bs-toast-bg: var(--ad-surface);
  --bs-toast-box-shadow: var(--ad-shadow-lg);
  --bs-toast-font-size: 0.875rem;
}

.modal { --bs-modal-border-radius: 12px; }
.modal-content { border-color: var(--ad-line); box-shadow: var(--ad-shadow-lg); }

.offcanvas { --bs-offcanvas-bg: var(--ad-rail); }

.tooltip {
  --bs-tooltip-bg: var(--ad-title);
  --bs-tooltip-border-radius: var(--ad-radius-sm);
  --bs-tooltip-font-size: 0.75rem;
}

.progress { --bs-progress-bar-bg: var(--color-primary); }

/* Alerts carry the API's own words about a failure, so they have to be
   noticeable — but a full-saturation banner across the top of a form is a
   scolding. Tinted plate, hairline, coloured glyph. */
.alert {
  --bs-alert-border-radius: var(--ad-radius);
  display: flex;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  font-size: 0.875rem;
}

.alert > .icon { flex: 0 0 auto; margin-block-start: 2px; font-size: 16px; }
.alert p:last-child { margin-block-end: 0; }

.alert-danger {
  --bs-alert-color: var(--bs-danger-text-emphasis);
  --bs-alert-bg: var(--bs-danger-bg-subtle);
  --bs-alert-border-color: var(--bs-danger-border-subtle);
}

.alert-warning {
  --bs-alert-color: var(--bs-warning-text-emphasis);
  --bs-alert-bg: var(--bs-warning-bg-subtle);
  --bs-alert-border-color: var(--bs-warning-border-subtle);
}


/* ==========================================================================
   8. CARDS

   One elevation for the whole console: a hairline and the faintest lift. What
   separates a card from the ground is that the ground is grey and the card is
   white, not that the card is floating two centimetres above it.
   ========================================================================== */
.card {
  --bs-card-border-color: var(--ad-line);
  --bs-card-border-radius: var(--ad-radius-lg);
  --bs-card-bg: var(--ad-surface);
  --bs-card-cap-bg: transparent;
  --bs-card-cap-padding-y: var(--space-3);
  --bs-card-cap-padding-x: var(--space-5);
  --bs-card-spacer-y: var(--space-5);
  --bs-card-spacer-x: var(--space-5);
  box-shadow: var(--ad-shadow-xs);
}

.card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  border-block-end-color: var(--ad-line);
  font-size: 0.875rem;
  font-weight: var(--fw-semibold);
  color: var(--ad-title);
}

/* The section glyph is a wayfinding mark, not an accent. It used to be brand
   green, which put a dozen green dots down a form whose one green thing should
   be the Save button. */
.card-header .icon { color: var(--ad-faint); font-size: 15px; }

.card-footer {
  border-block-start-color: var(--ad-line);
  padding: var(--space-3) var(--space-5);
}


/* ==========================================================================
   9. TABLES

   The console's main object. Column heads are labels rather than a filled
   band: a grey header bar competes with the card header directly above it,
   and there is nothing in a table that needs to outrank the rows.
   ========================================================================== */
.table {
  --bs-table-border-color: var(--ad-line);
  --bs-table-bg: transparent;
  --bs-table-color: var(--ad-text);
  --bs-table-hover-bg: var(--ad-sunken);
  --bs-table-hover-color: var(--ad-title);
  --bs-table-cell-padding-y: 0.6875rem;
  --bs-table-cell-padding-x: var(--space-5);
  margin-block-end: 0;
}

.table > thead th {
  padding-block: 0.5rem;
  border-block-end: 1px solid var(--ad-line);
  background: transparent;
  font-size: 0.6875rem;
  font-weight: var(--fw-semibold);
  text-transform: uppercase;
  letter-spacing: 0.055em;
  color: var(--ad-faint);
  white-space: nowrap;
}

/* A card whose body is a table: the last row's rule would double the card's
   own edge. */
.card > .table-responsive:last-child .table > tbody > tr:last-child > * {
  border-block-end-width: 0;
}

.table-hover > tbody > tr { transition: background-color var(--dur-fast) var(--ease); }


/* ==========================================================================
   10. BADGES AND CHIPS
   ========================================================================== */
.badge {
  --bs-badge-font-size: 0.6875rem;
  --bs-badge-font-weight: var(--fw-semibold);
  --bs-badge-padding-y: 0.3em;
  --bs-badge-padding-x: 0.65em;
  --bs-badge-border-radius: var(--radius-pill);

  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  letter-spacing: 0.01em;
  line-height: 1.45;
}

/*
   A state badge. StatusView.BadgeClass builds the rest of the class list out
   of Bootstrap's own subtle trio — `text-*-emphasis bg-*-subtle
   border-*-subtle` — which is the pattern Bootstrap 5.3 documents for exactly
   this. All this class adds is the dot, which is what lets the column be read
   down at a glance without reading any of the words.
*/
.badge-state::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: var(--radius-full);
  background: currentColor;
  flex: 0 0 auto;
}

/* "Nothing is happening here" — a draft, a count beside a heading. No dot: it
   is not reporting a state that anyone acts on. */
.badge-quiet {
  background-color: var(--ad-sunken);
  border: 1px solid var(--ad-line);
  color: var(--ad-muted);
}

/* One condition currently narrowing a list. Each is a link that drops its own
   condition, so it has to look pressable. */
.admin__chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.25rem 0.625rem;
  border: 1px solid var(--ad-line-strong);
  border-radius: var(--radius-pill);
  background: var(--ad-surface);
  color: var(--ad-text);
  font-size: 0.75rem;
  font-weight: var(--fw-medium);
  text-decoration: none;
  transition: var(--transition);
}

.admin__chip:hover {
  border-color: var(--ad-line-strong);
  background: var(--ad-sunken);
  color: var(--ad-title);
}

.admin__chip .icon { font-size: 9px; color: var(--ad-faint); }
.admin__chip:hover .icon { color: var(--ad-title); }


/* ==========================================================================
   11. SHELL — frame, sidebar, topbar

   A fixed rail and a scrolling panel. The rail is a surface separated by a
   hairline, not a coloured slab: the brand belongs on the public site, and in
   here the operator's own data should be the only saturated thing on screen.
   ========================================================================== */
.admin {
  display: grid;
  grid-template-columns: var(--ad-sidebar-w) minmax(0, 1fr);
  min-height: 100vh;
  background: var(--ad-ground);
}

.admin__panel {
  display: flex;
  flex-direction: column;
  min-width: 0;   /* lets a wide table scroll inside the panel rather than
                     stretching the grid column */
}

.admin__main {
  flex: 1 1 auto;
  padding: var(--space-6) var(--space-7) var(--space-10);
}

@media (max-width: 991.98px) {
  /* Below the breakpoint the rail is Bootstrap's offcanvas and is out of flow,
     so the grid drops to one column. */
  .admin { grid-template-columns: minmax(0, 1fr); }
  .admin__main { padding: var(--space-5) var(--space-4) var(--space-8); }
}

/* ---- Sidebar --------------------------------------------------------------
   One element, two behaviours: `offcanvas-lg` is a sliding panel below 992px
   and an ordinary block above it. */
.admin__sidebar {
  --bs-offcanvas-width: var(--ad-sidebar-w);
  --bs-offcanvas-bg: var(--ad-rail);
  --bs-offcanvas-padding-x: 0;
  --bs-offcanvas-padding-y: 0;

  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  background: var(--ad-rail);
  border-inline-end: 1px solid var(--ad-line);
}

@media (min-width: 992px) {
  .admin__sidebar {
    position: sticky;
    inset-block-start: 0;
    height: 100vh;
    overflow-y: auto;

    /* The only `!important` in this file, and it is not optional. Above the
       breakpoint `.offcanvas-lg` sets `background-color: transparent !important`
       — it is built to dissolve into a parent it is merely a section of. This
       sidebar *is* the parent, so the ground has to be taken back, and without
       this the rail is transparent and reads as a slightly-wrong shade of the
       app ground rather than as an obvious bug. */
    background: var(--ad-rail) !important;
  }
}

.admin__brand {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  height: var(--ad-topbar-h);
  flex: 0 0 auto;
  padding-inline: var(--space-4);
  border-block-end: 1px solid var(--ad-line);
  text-decoration: none;
}

/* The one place the brand green is a filled shape. It is 30px across and it is
   the only mark on the screen, which is the whole argument for taking it off
   the sidebar behind it. */
.admin__brand-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  flex: 0 0 auto;
  border-radius: var(--ad-radius);
  background: var(--color-primary);
  color: var(--ad-on-primary);
  box-shadow: var(--ad-shadow-xs);
}

.admin__brand-mark .icon { font-size: 17px; }

.admin__brand-name {
  display: block;
  font-size: 0.9375rem;
  font-weight: var(--fw-semibold);
  color: var(--ad-title);
  line-height: 1.2;
}

.admin__brand-sub {
  display: block;
  font-size: 0.625rem;
  font-weight: var(--fw-medium);
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--ad-faint);
}

.admin__nav { padding: var(--space-4) var(--space-3); }

.admin__nav-heading {
  margin-block: 0 var(--space-2);
  padding-inline: var(--space-2);
  font-size: 0.625rem;
  font-weight: var(--fw-semibold);
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--ad-faint);
}

.admin__nav .nav-link {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 0.4375rem var(--space-2);
  border-radius: var(--ad-radius-sm);
  font-size: 0.8125rem;
  font-weight: var(--fw-medium);
  color: var(--ad-text);
}

.admin__nav .nav-link .icon { flex: 0 0 auto; color: var(--ad-faint); }

.admin__nav .nav-link:hover {
  background: var(--ad-sunken);
  color: var(--ad-title);
}

.admin__nav .nav-link:hover .icon { color: var(--ad-muted); }

/* The active item. A tinted plate rather than a filled green pill — this is a
   place marker, and the pill made the sidebar look like it held the page's
   primary action. */
.admin__nav .nav-link.active,
.admin__nav .nav-pills .nav-link.active {
  background: var(--bs-primary-bg-subtle);
  color: var(--bs-primary-text-emphasis);
  font-weight: var(--fw-semibold);
}

.admin__nav .nav-link.active .icon { color: var(--color-primary); }

/* ---- Topbar --------------------------------------------------------------- */
.admin__topbar {
  position: sticky;
  inset-block-start: 0;
  z-index: var(--z-sticky);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  height: var(--ad-topbar-h);
  flex: 0 0 auto;
  padding-inline: var(--space-7);
  background: var(--ad-surface);
  border-block-end: 1px solid var(--ad-line);
}

@media (max-width: 991.98px) {
  .admin__topbar { padding-inline: var(--space-4); }
}

.admin__title {
  margin: 0;
  font-size: 0.9375rem;
  font-weight: var(--fw-semibold);
  color: var(--ad-title);
  letter-spacing: -0.006em;

  /* A long offer title in the topbar must not push the account menu off. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.admin__account {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.25rem 0.375rem;
  border: 0;
  border-radius: var(--ad-radius);
  background: none;
  transition: background-color var(--dur-fast) var(--ease);
}

.admin__account:hover,
.admin__account.show { background: var(--ad-sunken); }

.admin__avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  flex: 0 0 auto;
  border-radius: var(--radius-full);
  background: var(--bs-primary-bg-subtle);
  color: var(--bs-primary-text-emphasis);
  font-size: 0.75rem;
  font-weight: var(--fw-semibold);
  line-height: 1;
}

.admin__identity {
  flex-direction: column;
  align-items: flex-start;
  line-height: 1.25;
  text-align: start;
}

.admin__identity-name {
  font-size: 0.8125rem;
  font-weight: var(--fw-medium);
  color: var(--ad-title);
}

.admin__identity-role { font-size: 0.6875rem; color: var(--ad-muted); }

/* The colour-scheme control. A half-filled disc: the shape says "light and
   dark" without a sun and a moon that have to be swapped by script. */
.admin__scheme-dot {
  display: inline-block;
  width: 13px;
  height: 13px;
  border: 1px solid currentColor;
  border-radius: var(--radius-full);
  background: linear-gradient(to bottom, currentColor 0 50%, transparent 50% 100%);
}

/* A sidecar column that follows the form beside it. */
.admin__aside { top: calc(var(--ad-topbar-h) + var(--space-4)); }


/* ==========================================================================
   12. STAT TILES AND TABLE CELLS
   ========================================================================== */

/* The number is the question and the list is the answer, so the whole tile is
   a link to the list it counts. */
.admin__stat {
  display: flex;
  /* Explicit, and load-bearing: `.card` is itself a column flex container, so
     without this the glyph stacks under the number instead of sitting beside
     it. */
  flex-direction: row;
  align-items: flex-start;
  gap: var(--space-3);
  height: 100%;
  padding: var(--space-4) var(--space-5);
  text-decoration: none;
  transition: border-color var(--dur-fast) var(--ease),
              box-shadow var(--dur-fast) var(--ease);
}

.admin__stat:hover {
  border-color: var(--ad-line-strong);
  box-shadow: var(--ad-shadow-sm);
}

/* Pushed to the trailing edge by `order`, so the number leads the tile in both
   reading directions and the glyph is the thing you find second. */
.admin__stat-icon {
  order: 2;
  margin-inline-start: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  flex: 0 0 auto;
  border-radius: var(--ad-radius-sm);
  background: var(--ad-sunken);
  color: var(--ad-muted);
  font-size: 15px;
}

.admin__stat-value {
  display: block;
  font-size: 1.625rem;
  font-weight: var(--fw-semibold);
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--ad-title);
}

.admin__stat-label {
  display: block;
  margin-block-start: 2px;
  font-size: 0.8125rem;
  color: var(--ad-muted);
}

/*
   The one tile that means someone has to do something. It gets a coloured
   glyph and a coloured edge — not a filled amber card. A whole tinted panel
   said "warning" at the same volume whether the number was 40 or 0, and the
   dashboard's job is to make one number stand out from three others, which a
   coloured 30px square does.
*/
.admin__stat--attention {
  border-color: var(--bs-warning-border-subtle);
}

.admin__stat--attention .admin__stat-icon {
  background: var(--bs-warning-bg-subtle);
  color: var(--bs-warning-text-emphasis);
}

.admin__stat--attention .admin__stat-value { color: var(--bs-warning-text-emphasis); }

.admin__stat--info .admin__stat-icon {
  background: var(--bs-info-bg-subtle);
  color: var(--bs-info-text-emphasis);
}

/* ---- Cells ---------------------------------------------------------------- */
.admin__cell-link { text-decoration: none; }

.admin__cell-title {
  display: block;
  font-weight: var(--fw-medium);
  color: var(--ad-title);
  transition: color var(--dur-fast) var(--ease);
}

a.admin__cell-link:hover .admin__cell-title { color: var(--color-primary); }

/* The slug under a title. Deliberately small and faint: it is there to be
   checked, not read. */
.admin__cell-sub {
  display: block;
  margin-block-start: 1px;
  font-size: 0.6875rem;
  color: var(--ad-faint);
}

/*
   A discount figure. Set in the brand green and given a slightly heavier weight
   rather than a badge: it is a measurement in a column of measurements, and the
   red pill it used to be made the best number on the row look like a fault.
*/
.admin__cell-discount {
  font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums;
  color: var(--bs-success-text-emphasis);
}

.admin__cell-dates { color: var(--ad-text); }
.admin__cell-dates--past { color: var(--ad-faint); }

/* An actions column takes the width of its buttons and no more. */
.admin__cell-actions { width: 1%; white-space: nowrap; text-align: end; }

.admin__tree-mark { color: var(--ad-faint); font-size: 11px; }

/* Two lines and then an ellipsis. The audit and security logs carry a user
   agent and a bag of key/value data whose length nobody controls, and one long
   row is enough to double the height of every row beside it. The full value is
   in the `title`, and the row is still scannable. */
.admin__cell-clamp {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  max-width: 42ch;
}

/* Two one-button forms inside one `.btn-group`. A write has to be a POST and a
   form cannot be nested, so the category reorder controls are two forms rather
   than two buttons — which leaves the group with block-level children instead
   of the buttons it styles, and its joined corners come apart. `display:
   contents` hands the buttons back to the group. */
.admin__reorder > form { display: contents; }

/* ---- Empty states --------------------------------------------------------- */
.admin__empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  padding-block: var(--space-9);
  text-align: center;
}

.admin__empty-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  margin-block-end: var(--space-2);
  border-radius: var(--radius-full);
  background: var(--ad-sunken);
  color: var(--ad-muted);
  font-size: 20px;
}

.admin__empty-hint {
  max-width: 46ch;
  font-size: 0.8125rem;
  color: var(--ad-muted);
}

.admin__reference {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--ad-faint);
}


/* ==========================================================================
   13. MEDIA

   Shared by the library screen and by every picker — the offer gallery, the
   merchant's logo and cover, the category image — because they all draw the
   same thing: a square frame with an image fitted into it.
   ========================================================================== */

/* Fixed ratio, so a page of mixed portrait and landscape uploads still reads
   as a grid. */
.media__frame {
  position: relative;
  display: block;
  aspect-ratio: 1;
  overflow: hidden;
  border: 1px solid var(--ad-line);
  border-radius: var(--ad-radius);
  background: var(--ad-sunken);
}

.media__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--dur-slow) var(--ease-out);
}

.media__frame:hover img { transform: scale(1.04); }

.media__frame::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgb(12 14 18 / 32%);
  opacity: 0;
  transition: var(--transition);
}

.media__frame:hover::after { opacity: 1; }

.media__zoom {
  position: absolute;
  z-index: 1;
  inset-block-end: var(--space-2);
  inset-inline-end: var(--space-2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-full);
  background: var(--ad-surface);
  color: var(--ad-title);
  box-shadow: var(--ad-shadow-sm);
  opacity: 0;
  transform: translateY(6px);
  transition: var(--transition);
}

.media__frame:hover .media__zoom { opacity: 1; transform: none; }

/* A slot rather than a grid tile. The merchant and category forms show one
   chosen image beside its buttons, where a full-width square would be a
   photograph in a place that wanted a thumbnail. */
.media__frame--small { flex: 0 0 auto; width: 96px; }

/* The empty half of a slot. `admin__empty-icon` already draws the plate; this
   only centres it in a frame with no image to fill it. */
.media__frame > .admin__empty-icon {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  border-radius: 0;
  background: none;
}

/* The hero flag on gallery position 0. */
.media__hero-badge {
  position: absolute;
  z-index: 1;
  inset-block-start: var(--space-2);
  inset-inline-start: var(--space-2);
}

.media__name {
  display: block;
  font-size: 0.8125rem;
  font-weight: var(--fw-medium);
  color: var(--ad-title);

  /* A long upload name must not stretch its grid column. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}


/* ==========================================================================
   14. SIGN-IN, NOTICES, VALIDATION
   ========================================================================== */
.signin {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  min-height: 100vh;
  background: var(--ad-surface);
}

/*
   The one deep-ink surface in the product, and the only place the brand is
   allowed to fill a whole panel: there is no data on this screen to compete
   with, and the operator should be able to tell at a glance which deployment
   they are signing in to.
*/
.signin__brand {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--space-5);
  padding: var(--space-12) var(--space-10);

  /* Fixed ink and fixed white, in both schemes: this panel is deliberately not
     part of the ramp. It is the only surface in the product that does not
     follow the operator's colour scheme, because there is nothing on it to
     read against — see the note above. */
  background: #10131a;
  color: rgb(255 255 255 / 72%);

  /* A soft green wash off the start corner, so the panel is not a flat slab.
     `at 0% 0%` is a position inside the box, not a reading direction — the
     gradient reads the same either way and is deliberately not mirrored. */
  background-image: radial-gradient(circle at 0% 0%, rgba(var(--bs-primary-rgb), 0.35), transparent 60%);
}

.signin__brand h1,
.signin__brand .h1 { color: #fff; }

.signin__mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  border-radius: 14px;
  background: var(--color-primary);
  color: var(--ad-on-primary);
  font-size: 24px;
}

/* The light end of the brand ramp, not `--color-primary`: this panel is ink in
   both schemes, and the light scheme's deep green all but disappears on it. */
.signin__points .icon { color: var(--green-400); }

.signin__panel {
  display: grid;
  place-items: center;
  padding: var(--space-8) var(--space-6);
  background: var(--ad-surface);
}

.signin__card { width: min(384px, 100%); }

/* Decoration. Dropped rather than stacked below 860px — on a phone it would
   push the form, which is the only thing this screen exists for, under the
   fold. */
@media (max-width: 860px) {
  .signin { grid-template-columns: minmax(0, 1fr); }
  .signin__brand { display: none; }
}

.notice {
  display: grid;
  place-items: center;
  min-height: 100vh;
  padding: var(--space-6);
  background: var(--ad-ground);
}

/* --------------------------------------------------------------------------
   VALIDATION

   jQuery Unobtrusive Validation writes these two classes and no others. They
   are mapped onto Bootstrap's own invalid styling so a message from the client
   and one from the server look identical — which is the point: the client check
   is a saved round trip, not a different check.
   -------------------------------------------------------------------------- */
.form-control.input-validation-error,
.form-select.input-validation-error,
.form-check-input.input-validation-error {
  border-color: rgb(var(--bs-danger-rgb));
}

.form-control.input-validation-error:focus,
.form-select.input-validation-error:focus {
  border-color: rgb(var(--bs-danger-rgb));
  box-shadow: 0 0 0 var(--bs-focus-ring-width) rgba(var(--bs-danger-rgb), 0.18);
}

.field-validation-error {
  display: block;
  margin-block-start: 0.375rem;
  font-size: 0.75rem;
  color: rgb(var(--bs-danger-rgb));
}

.field-validation-valid:empty { display: none; }

/* The reveal button on the sign-in form marks itself pressed rather than
   swapping in a glyph that means something else. */
[data-reveal][aria-pressed="true"] { color: var(--color-primary); }


/* ==========================================================================
   15. REPORTING

   The analytics screens, and the one place in this console that draws a chart.

   There is no charting library here and that is deliberate. A sixth vendored
   script would have to be kept, sized, themed and — the expensive part — taught
   to mirror, because every one of them lays its axis out in physical pixels.
   The daily series is two bars per day in a flex row instead: laid out in
   logical order, so it reads oldest-first in both directions with no rule of
   its own, and rendered from markup a screen reader can read (D-62).

   The only inline style in this UI is the height percentage on each bar, which
   is the measurement itself and cannot live in a stylesheet.
   ========================================================================== */

/* A measured figure. Tabular so a column of them lines up on the decimal, and
   `ltr` is *not* set here — the digits are already the same glyphs in both
   languages, and forcing a direction would strand a percent sign on the wrong
   side of an Arabic column. */
.admin__figure {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.admin__chart {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  min-height: 168px;
  margin: 0;
  padding: 0;
  list-style: none;
  overflow-x: auto;
}

.admin__chart-day {
  display: flex;
  flex: 1 0 18px;
  flex-direction: column;
  align-items: center;
  gap: var(--space-1);
  min-width: 18px;
}

/* The bars sit on a sunken plate so a day with no activity is still a visible
   column rather than a gap the eye reads as missing data. */
.admin__chart-bars {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  width: 100%;
  height: 132px;
  padding: 2px;
  border-radius: var(--ad-radius-sm);
  background: var(--ad-sunken);
}

.admin__chart-bar {
  flex: 1 1 0;
  min-height: 1px;
  border-radius: 2px 2px 0 0;
}

.admin__chart-bar--views { background: var(--color-primary); }

/* Clicks are the subset worth noticing, so they take the accent rather than a
   lighter tint of the same green — two greens at these widths are one green. */
.admin__chart-bar--clicks { background: var(--bs-info-text-emphasis); }

.admin__chart-label {
  font-size: 0.625rem;
  color: var(--ad-faint);
  font-variant-numeric: tabular-nums;
}

.admin__legend {
  display: inline-flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  font-size: 0.75rem;
  color: var(--ad-muted);
}

.admin__legend-item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}

.admin__legend-swatch {
  width: 10px;
  height: 10px;
  border-radius: 2px;
}

.admin__legend-swatch--views { background: var(--color-primary); }
.admin__legend-swatch--clicks { background: var(--bs-info-text-emphasis); }
