﻿/* ==========================================================================
   ATS Kendo design tokens
   Taken verbatim from the ORBITSRegReports project so both apps render Telerik
   components — the data grids in particular — from one palette. Update them
   there and copy across, rather than diverging here.
   ========================================================================== */

:root {
    /* ATS Theme - Color Palette (matched to american-tradeshow.com: navy #002D74 + orange #F18A00) */
    --kendo-color-app-surface: #F7F9FC;
    --kendo-color-on-app-surface: #12203D;
    --kendo-color-subtle: #5A6478;
    --kendo-color-surface: #FFFFFF;
    --kendo-color-surface-alt: #EEF2F8;
    --kendo-color-on-surface: #12203D;
    --kendo-color-on-surface-alt: #1B2B4D;
    --kendo-color-border: #C7CEDA;
    --kendo-color-border-alt: #D3D9E3;
    --kendo-color-base: #64748B;
    --kendo-color-base-hover: #55627A;
    --kendo-color-base-active: #46536B;
    --kendo-color-base-subtle: #EDF1F7;
    --kendo-color-base-subtle-hover: #E1E7F0;
    --kendo-color-base-subtle-active: #CBD5E3;
    --kendo-color-on-base: #FFFFFF;
    --kendo-color-base-emphasis: #55627A;
    --kendo-color-base-on-subtle: #3B4657;
    --kendo-color-base-on-surface: #46536B;
    --kendo-color-primary: #002D74;
    --kendo-color-primary-hover: #003D99;
    --kendo-color-primary-active: #001F52;
    --kendo-color-primary-subtle: #DCE6F5;
    --kendo-color-primary-subtle-hover: #C7D7EF;
    --kendo-color-primary-subtle-active: #B0C6E8;
    --kendo-color-on-primary: #FFFFFF;
    --kendo-color-primary-emphasis: #1A4A8F;
    --kendo-color-primary-on-subtle: #002D74;
    --kendo-color-primary-on-surface: #002D74;
    --kendo-color-secondary: #55627A;
    --kendo-color-secondary-hover: #4A5568;
    --kendo-color-secondary-active: #3F4A5C;
    --kendo-color-secondary-subtle: #E7EAF0;
    --kendo-color-secondary-subtle-hover: #DCE0E9;
    --kendo-color-secondary-subtle-active: #CDD3E0;
    --kendo-color-on-secondary: #FFFFFF;
    --kendo-color-secondary-emphasis: #6B7690;
    --kendo-color-secondary-on-subtle: #3F4A5C;
    --kendo-color-secondary-on-surface: #55627A;
    --kendo-color-tertiary: #F18A00;
    --kendo-color-tertiary-hover: #D97900;
    --kendo-color-tertiary-active: #C06B00;
    --kendo-color-tertiary-subtle: #FCE8CC;
    --kendo-color-tertiary-subtle-hover: #FADCB0;
    --kendo-color-tertiary-subtle-active: #F7CD8F;
    --kendo-color-on-tertiary: #1F2328;
    --kendo-color-tertiary-emphasis: #C06B00;
    --kendo-color-tertiary-on-subtle: #8A4E00;
    --kendo-color-tertiary-on-surface: #B36A00;
    --kendo-color-info: #2B6CB0;
    --kendo-color-info-hover: #245D98;
    --kendo-color-info-active: #1F4F81;
    --kendo-color-info-subtle: #DCEBFA;
    --kendo-color-info-subtle-hover: #C6DEFA;
    --kendo-color-info-subtle-active: #B0D0F7;
    --kendo-color-on-info: #FFFFFF;
    --kendo-color-info-emphasis: #4A87C4;
    --kendo-color-info-on-subtle: #17436F;
    --kendo-color-info-on-surface: #2B5E99;
    --kendo-color-success: #2E7D32;
    --kendo-color-success-hover: #276B2B;
    --kendo-color-success-active: #215D25;
    --kendo-color-success-subtle: #DCEFD8;
    --kendo-color-success-subtle-hover: #C8E4C2;
    --kendo-color-success-subtle-active: #B3D8AC;
    --kendo-color-on-success: #FFFFFF;
    --kendo-color-success-emphasis: #4b954e;
    --kendo-color-success-on-subtle: #1E5422;
    --kendo-color-success-on-surface: #2C6730;
    --kendo-color-warning: #B7791F;
    --kendo-color-warning-hover: #A5671A;
    --kendo-color-warning-active: #915914;
    --kendo-color-warning-subtle: #F8E7C7;
    --kendo-color-warning-subtle-hover: #F3D8A4;
    --kendo-color-warning-subtle-active: #EDC978;
    --kendo-color-on-warning: #fdfdfd;
    --kendo-color-warning-emphasis: #a97d2f;
    --kendo-color-warning-on-subtle: #6B4710;
    --kendo-color-warning-on-surface: #8A5911;
    --kendo-color-error: #C0392B;
    --kendo-color-error-hover: #A93226;
    --kendo-color-error-active: #922B21;
    --kendo-color-error-subtle: #F7D7D3;
    --kendo-color-error-subtle-hover: #F1C2BC;
    --kendo-color-error-subtle-active: #E8ADA4;
    --kendo-color-on-error: #FFFFFF;
    --kendo-color-error-emphasis: #cf5748;
    --kendo-color-error-on-subtle: #7A1F16;
    --kendo-color-error-on-surface: #9E2F24;
    --kendo-color-light: #8A93A6;
    --kendo-color-light-hover: #8B94A5;
    --kendo-color-light-active: #8D95A2;
    --kendo-color-light-subtle: #FFFFFF;
    --kendo-color-light-subtle-hover: #F8FAFC;
    --kendo-color-light-subtle-active: #F0F3F8;
    --kendo-color-on-light: #12203D;
    --kendo-color-light-emphasis: #949CAB;
    --kendo-color-light-on-subtle: #3F4A5C;
    --kendo-color-light-on-surface: #5A6478;
    --kendo-color-dark: #14213D;
    --kendo-color-dark-hover: #0F1A30;
    --kendo-color-dark-active: #0B1425;
    --kendo-color-dark-subtle: #E4E8EF;
    --kendo-color-dark-subtle-hover: #D2D8E3;
    --kendo-color-dark-subtle-active: #C0C8D8;
    --kendo-color-on-dark: #FFFFFF;
    --kendo-color-dark-emphasis: #333F58;
    --kendo-color-dark-on-subtle: #12203D;
    --kendo-color-dark-on-surface: #14213D;
    --kendo-color-inverse: #12203D;
    --kendo-color-inverse-hover: #0D1830;
    --kendo-color-inverse-active: #091226;
    --kendo-color-inverse-subtle: #E5E9EF;
    --kendo-color-inverse-subtle-hover: #D4DAE3;
    --kendo-color-inverse-subtle-active: #C2CAD6;
    --kendo-color-on-inverse: #FFFFFF;
    --kendo-color-inverse-emphasis: #333F58;
    --kendo-color-inverse-on-subtle: #12203D;
    --kendo-color-inverse-on-surface: #12203D;
    --kendo-color-series-a: #002D74;
    --kendo-color-series-b: #F18A00;
    --kendo-color-series-c: #55627A;
    --kendo-color-series-d: #2B6CB0;
    --kendo-color-series-e: #2E7D32;
    --kendo-color-series-f: #C0392B;

    /* Typography */
    --kendo-font-family-sans: Inter, 'Helvetica Neue', Helvetica, Arial, sans-serif;
    --kendo-font-family-serif: Georgia;
    /* A real stack. This named JetBrains Mono, which is neither loaded nor installed on
       Windows, so anything monospaced silently fell back to whatever the browser chose. */
    --kendo-font-family-monospace: ui-monospace, SFMono-Regular, Consolas, 'Liberation Mono', monospace;

    /* Border Radius. All one value, so the app's own chrome and Telerik's agree: these are the
       theme's own variables, so buttons, inputs, dropdowns and the grid take the same corner as
       everything else rather than the 4 / 6 / 8px ladder they shipped with.

       The whole ladder has to be here, not just sm / md / lg. Meridian also ships xl (12px) and
       xxl (16px), and leaving those at the theme's values is what made the grid toolbar look
       wrong: .k-grid-toolbar rounds its top corners with xxl while .k-grid rounds its own with
       lg, so a 16px curve sat inside a 10px one and pulled away from the border, leaving a
       crescent of the page showing through each top corner. It is on every grid in the app; the
       detail grid an expanded Exhibitor List row drops open is just where it shows, because
       there the crescent opens onto a coloured row rather than onto white.

       xxl also carries the cards, windows, popovers and the calendar; xl the popups, tabs and
       the expander. All of them were meant to be 10px too. */
    --kendo-border-radius-sm: 10px;
    --kendo-border-radius-md: 10px;
    --kendo-border-radius-lg: 10px;
    --kendo-border-radius-xl: 10px;
    --kendo-border-radius-xxl: 10px;

    /* Elevation (Shadows) */
    --kendo-elevation-1: 0 2px 6px rgba(18,32,61,0.08);
    --kendo-elevation-2: 0 4px 12px rgba(18,32,61,0.10);
    --kendo-elevation-3: 0 8px 24px rgba(18,32,61,0.14);

    /* App header brand color - stays navy regardless of theme, independent of --kendo-color-primary */
    --app-header-bg: #002D74;
    --app-header-on-bg: #FFFFFF;
}

:root[data-theme="dark"] {
    /* ATS Theme - Dark Palette */
    --kendo-color-app-surface: #0B1220;
    --kendo-color-on-app-surface: #E4E8F0;
    --kendo-color-subtle: #94A3B8;
    --kendo-color-surface: #121A2B;
    --kendo-color-surface-alt: #1A2540;
    --kendo-color-on-surface: #E4E8F0;
    --kendo-color-on-surface-alt: #E4E8F0;
    --kendo-color-border: #2B3550;
    --kendo-color-border-alt: #333F5C;
    --kendo-color-base: #8A93A6;
    --kendo-color-base-hover: #9BA3B4;
    --kendo-color-base-active: #ACB3C1;
    --kendo-color-base-subtle: #1E293B;
    --kendo-color-base-subtle-hover: #263047;
    --kendo-color-base-subtle-active: #2E3950;
    --kendo-color-on-base: #0B1220;
    --kendo-color-base-emphasis: #AEB6C4;
    --kendo-color-base-on-subtle: #C7CEDA;
    --kendo-color-base-on-surface: #C7CEDA;
    --kendo-color-primary: #5B8DEF;
    --kendo-color-primary-hover: #6B9CF2;
    --kendo-color-primary-active: #3D6FD1;
    --kendo-color-primary-subtle: #1B2C4D;
    --kendo-color-primary-subtle-hover: #22355C;
    --kendo-color-primary-subtle-active: #2A3F6C;
    --kendo-color-on-primary: #06122B;
    --kendo-color-primary-emphasis: #8FB2F5;
    --kendo-color-primary-on-subtle: #9CC0FF;
    --kendo-color-primary-on-surface: #9CC0FF;
    --kendo-color-secondary: #8A93A6;
    --kendo-color-secondary-hover: #9BA3B4;
    --kendo-color-secondary-active: #ACB3C1;
    --kendo-color-secondary-subtle: #1E293B;
    --kendo-color-secondary-subtle-hover: #263047;
    --kendo-color-secondary-subtle-active: #2E3950;
    --kendo-color-on-secondary: #0B1220;
    --kendo-color-secondary-emphasis: #AEB6C4;
    --kendo-color-secondary-on-subtle: #C7CEDA;
    --kendo-color-secondary-on-surface: #C7CEDA;
    --kendo-color-tertiary: #FFA733;
    --kendo-color-tertiary-hover: #FFB84D;
    --kendo-color-tertiary-active: #E0940A;
    --kendo-color-tertiary-subtle: #4A2F00;
    --kendo-color-tertiary-subtle-hover: #5C3B00;
    --kendo-color-tertiary-subtle-active: #6E4700;
    --kendo-color-on-tertiary: #1F2328;
    --kendo-color-tertiary-emphasis: #FFC670;
    --kendo-color-tertiary-on-subtle: #FFB84D;
    --kendo-color-tertiary-on-surface: #FFB84D;
    --kendo-color-info: #4A9EFF;
    --kendo-color-info-hover: #6BB0FF;
    --kendo-color-info-active: #2E86F0;
    --kendo-color-info-subtle: #123152;
    --kendo-color-info-subtle-hover: #163C63;
    --kendo-color-info-subtle-active: #1B4874;
    --kendo-color-on-info: #06111F;
    --kendo-color-info-emphasis: #7DBBFF;
    --kendo-color-info-on-subtle: #9CCBFF;
    --kendo-color-info-on-surface: #9CCBFF;
    --kendo-color-success: #4CAF50;
    --kendo-color-success-hover: #66BB6A;
    --kendo-color-success-active: #388E3C;
    --kendo-color-success-subtle: #14311A;
    --kendo-color-success-subtle-hover: #1A3D21;
    --kendo-color-success-subtle-active: #204928;
    --kendo-color-on-success: #06120A;
    --kendo-color-success-emphasis: #81C784;
    --kendo-color-success-on-subtle: #A5D6A7;
    --kendo-color-success-on-surface: #A5D6A7;
    --kendo-color-warning: #F0A93E;
    --kendo-color-warning-hover: #F5BB61;
    --kendo-color-warning-active: #D9931F;
    --kendo-color-warning-subtle: #3A2A0D;
    --kendo-color-warning-subtle-hover: #473411;
    --kendo-color-warning-subtle-active: #543E14;
    --kendo-color-on-warning: #1F1503;
    --kendo-color-warning-emphasis: #F7C57D;
    --kendo-color-warning-on-subtle: #F5BB61;
    --kendo-color-warning-on-surface: #F5BB61;
    --kendo-color-error: #E5564A;
    --kendo-color-error-hover: #EC7268;
    --kendo-color-error-active: #D6392C;
    --kendo-color-error-subtle: #3A1512;
    --kendo-color-error-subtle-hover: #471A16;
    --kendo-color-error-subtle-active: #54201B;
    --kendo-color-on-error: #1F0908;
    --kendo-color-error-emphasis: #F08A81;
    --kendo-color-error-on-subtle: #F3A69F;
    --kendo-color-error-on-surface: #F3A69F;
    --kendo-color-light: #2A3448;
    --kendo-color-light-hover: #333F58;
    --kendo-color-light-active: #3D4A66;
    --kendo-color-light-subtle: #121A2B;
    --kendo-color-light-subtle-hover: #1A2540;
    --kendo-color-light-subtle-active: #212D48;
    --kendo-color-on-light: #E4E8F0;
    --kendo-color-light-emphasis: #4A5568;
    --kendo-color-light-on-subtle: #C7CEDA;
    --kendo-color-light-on-surface: #94A3B8;
    --kendo-color-dark: #E4E8F0;
    --kendo-color-dark-hover: #F0F3F8;
    --kendo-color-dark-active: #FFFFFF;
    --kendo-color-dark-subtle: #1E293B;
    --kendo-color-dark-subtle-hover: #263047;
    --kendo-color-dark-subtle-active: #2E3950;
    --kendo-color-on-dark: #0B1220;
    --kendo-color-dark-emphasis: #C7CEDA;
    --kendo-color-dark-on-subtle: #E4E8F0;
    --kendo-color-dark-on-surface: #E4E8F0;
    --kendo-color-inverse: #E4E8F0;
    --kendo-color-inverse-hover: #F0F3F8;
    --kendo-color-inverse-active: #FFFFFF;
    --kendo-color-inverse-subtle: #1E293B;
    --kendo-color-inverse-subtle-hover: #263047;
    --kendo-color-inverse-subtle-active: #2E3950;
    --kendo-color-on-inverse: #0B1220;
    --kendo-color-inverse-emphasis: #C7CEDA;
    --kendo-color-inverse-on-subtle: #E4E8F0;
    --kendo-color-inverse-on-surface: #E4E8F0;
    --kendo-color-series-a: #5B8DEF;
    --kendo-color-series-b: #FFA733;
    --kendo-color-series-c: #94A3B8;
    --kendo-color-series-d: #4A9EFF;
    --kendo-color-series-e: #4CAF50;
    --kendo-color-series-f: #E5564A;

    --kendo-elevation-1: 0 2px 6px rgba(0,0,0,0.4);
    --kendo-elevation-2: 0 4px 12px rgba(0,0,0,0.5);
    --kendo-elevation-3: 0 8px 24px rgba(0,0,0,0.6);

    --app-header-bg: #0A1B3D;
    --app-header-on-bg: #E4E8F0;
}


/* ==========================================================================
   ExpoMetrics — design tokens
   The palette, typography and layout metrics carried over from the legacy
   Site.css / sb-admin-2 theme.
   ========================================================================== */

:root {
    /* Brand */
    --ats-navy: #002d74;
    --ats-orange: #f59420;
    --ats-orange-dark: #f18a00;
    --ats-blue: #374780;
    --ats-lite-blue: #5a6091;

    /* Surfaces */
    --surface-page: #ffffff;
    --surface-panel: whitesmoke;
    --surface-muted: whitesmoke;
    --surface-footer: #f5f5f5;
    --border-muted: #dddddd;

    /* Text */
    --text-body: #333333;
    --text-on-navy: whitesmoke;
    --text-muted: #6b7280;

    /* Metrics */
    /* ORBITSRegReports' rail, 280px, beside a header that is 56px on a phone - 32px of logo
       inside 12px of padding top and bottom - and 100px on desktop, where the wordmark runs
       at 200px wide; see the min-width block at the end of the file, which raises it. */
    --sidebar-width: 280px;
    --navbar-height: 56px;

    /* What a grid passes to Telerik's Height parameter. Virtual scrolling — the pager's "All"
       — refuses to start without one: "Both Height and RowHeight have to be set to use the
       Virtualization feature of Telerik Grid", thrown from OnParametersSetAsync, which takes
       the circuit down with it. A height coming from CSS does not satisfy that check, so the
       value has to travel through the parameter, and a parameter cannot carry a media query.
       A custom property can, which is what this is.

       Below the Fill breakpoint the page scrolls and the panel body is auto, so 100% would
       resolve to auto and leave the grid nothing to virtualise against; a viewport-relative
       height stands in there. svh rather than vh: a phone counts its collapsing address bar
       inside vh, which would run the grid past the bottom of the screen at rest. */
    --expo-grid-height: 70vh;

    /* The single source of truth for "is this the narrow layout". The grid pager's input type
       is a C# parameter, which CSS cannot set, so viewport.js reads this property back out
       rather than carrying a second copy of the threshold. Keep it in step with the 767px
       queries below. */
    --viewport: wide;

    /* Slim shell (Login, Forgot Password, errors). The height matches 300px at the logo's
       452x144 aspect; the slim page's bottom padding is calculated from both. */
    --slim-logo-width: 300px;
    --slim-logo-height: 96px;
    --slim-brand-gap: 26px;
    /* One corner radius for the whole app. These three were 4 / 6 / 16px, picking a different
       curve for a label, a panel and the page shell; they are kept as separate names only
       because the rules that reference them read better for it.

       px, not em: an em-based radius renders a different corner on every element, because it
       resolves against that element's own font-size rather than one value. */
    --radius-sm: 10px;
    --radius-md: 10px;
    --radius-page: 10px;

    /* ORBITS' body face, so the two apps read the same. Legacy ExpoMetrics named Hind Guntur
       and Play here; neither is installed on Windows or self-hosted, so that stack was
       resolving to Play for anything it actually reached. */
    --font-body: var(--kendo-font-family-sans);

    /* Navy as ink: links, focus rings, the login text. Its own name because navy as a ground
       (the bar, the login plate) stays navy in the dark theme, and navy text on the dark ground
       does not read. */
    --expo-ink-navy: var(--ats-navy);
    --text-strong: #000;
    --surface-raised: #fff;

    /* The login shell follows the theme, as ATSHub's does. These are the light values; the dark
       block below swaps them. */
    --expo-slim-ground: #efeeec;
    --expo-login-card: #fff;
    --expo-login-card-shadow: 0 6px 22px rgba(18, 32, 61, 0.12);
    --expo-login-field-bg: #fff;
    --expo-login-field-border: #d5d9e0;

    /* Native controls (a bare input, a select, the scrollbars) follow the theme rather than the
       OS. App.razor's meta says "light dark", which alone lets a dark OS paint them dark under
       a light theme; this pins them to whichever palette is live. ATSHub's rule. */
    color-scheme: light;
}

/* Dark theme: the ORBITS Kendo palette in the :root[data-theme="dark"] block at the top of the
   file, which theme.js switches on, plus this app's own names resolved onto it. Light values
   are untouched; everything here only applies once the user picks dark (or their OS does and
   they have not picked). */
:root[data-theme="dark"] {
    color-scheme: dark;

    --surface-page: var(--kendo-color-app-surface);
    --surface-panel: var(--kendo-color-surface-alt);
    --surface-muted: var(--kendo-color-surface-alt);
    --surface-footer: var(--kendo-color-surface-alt);
    --surface-raised: var(--kendo-color-surface);
    --border-muted: var(--kendo-color-border);

    --text-body: var(--kendo-color-on-app-surface);
    --text-strong: var(--kendo-color-on-app-surface);
    --text-muted: var(--kendo-color-subtle);

    /* The two slate blues are only ever text and hairlines, and at #374780 / #5a6091 neither
       reads on the dark ground. */
    --ats-blue: var(--kendo-color-primary-on-surface);
    --ats-lite-blue: var(--kendo-color-subtle);
    --expo-ink-navy: var(--kendo-color-primary-on-surface);

    --expo-slim-ground: var(--kendo-color-app-surface);
    --expo-login-card: var(--kendo-color-surface);
    --expo-login-card-shadow: var(--kendo-elevation-2);
    --expo-login-field-bg: var(--kendo-color-surface-alt);
    --expo-login-field-border: var(--kendo-color-border);
}

/* ==========================================================================
   Base
   ========================================================================== */

html, body {
    margin: 0;
    padding: 0;
}

/* .k-body is how the Meridian theme sets its own font-family, and a class outranks a bare
   `body`, so the face has to be claimed at that specificity or the declaration below is
   decorative. Both stacks lead with Inter, but this makes the choice ours rather than the
   theme's. */
body,
body.k-body {
    /* The bar's colour, which shows round the page's top-left corner. */
    background-color: var(--app-header-bg);
    color: var(--text-body);
    font-family: var(--font-body);
    font-size: 16px;
    border: none;
    overflow-y: auto;
    min-height: 100vh;
}

a {
    color: var(--expo-ink-navy);
}

h1, h2, h3, h4 {
    margin-top: 0;
}

/* A visible focus ring everywhere, as a floor. Only the login fields, the dashboard cards and
   the sidebar links authored one, so most of the app relied on the browser default — which
   Telerik's reset removes on its own controls. Navy because it is the app's darkest brand
   colour and reads at 12.9:1 on every light surface here; the sidebar overrides it with white,
   since navy on navy is nothing. */
:where(a, button, [role="button"], input, select, textarea, summary, [tabindex]):focus-visible {
    outline: 2px solid var(--expo-ink-navy);
    outline-offset: 2px;
}

/* Selection is a browser default that belongs to no design system until someone claims it.
   Only the background is set, so selected text keeps whatever contrast it already had. */
::selection {
    background-color: rgba(245, 148, 32, 0.32);
}

/* ==========================================================================
   App shell - navbar / sidebar
   ==========================================================================

   The treatment is ORBITSRegReports', so the two apps read as one product: a Fleet Navy
   header across the top of the content column, a 280px Surface Alt rail beside it with a
   hairline on its right edge, quiet rows that light up Fleet Navy Subtle when current, and
   uppercase tracked eyebrows on the section labels. Every number here was measured off the
   live site at 1440px and checked against NavMenu.razor.css and MainLayout.razor.css in that
   repo - see its DESIGN.md, "Navigation (sidebar)". The token block above is the same one it
   is written against, so the colours match by construction rather than by eye.

   Two radii are written as numbers rather than through the app's radius tokens: ORBITS rounds
   a nav row at 6px and a card at 8px, and this app's tokens are 10px for everything. The
   rail is the one place the two systems meet on screen, so it takes the reference's figures.
   -------------------------------------------------------------------------- */

/* Pinned to the top so the show, the menu toggle and the account menu stay reachable while a
   long report scrolls. Sticky rather than fixed: the bar keeps its place in flow, so the
   fixed sidebar's `top: var(--navbar-height)` still lines up with the bottom of it instead of
   leaving a navy gap once the page scrolls. Above the sidebar's z-index, below Telerik's
   popups, which render into their own container. */
.expo-navbar {
    display: flex;
    align-items: center;
    gap: 1rem;

    /* border-box so the bar measures exactly --navbar-height whatever it carries; everything
       sizing itself against the token, the full-height grid pages most visibly, misses by the
       border otherwise. */
    box-sizing: border-box;
    min-height: var(--navbar-height);
    padding: 0.75rem 1rem;
    /* --app-header-bg rather than primary: the bar stays navy in both themes, where dark
       primary is a light blue. */
    background-color: var(--app-header-bg);
    color: #fff;
    border: none;

    /* Elevation 2: one level up from a resting card, since the bar is structural and always
       on screen. The rail beside it carries no shadow of its own, so this is the one that
       separates the chrome from the page. */
    box-shadow: var(--kendo-elevation-2);
    position: sticky;
    top: 0;
    z-index: 4;
}

/* The menu toggle: Kendo's flat icon button, as the reference draws it - white on the navy,
   a 6px corner, and a wash of white on hover rather than a fill. Only on screen below 992px,
   where the rail is off-canvas; see the min-width block below. */
.expo-navbar__toggle {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 38px;
    min-height: 30px;
    padding: 6px 10px;
    background: none;
    border: 1px solid transparent;
    border-radius: 6px;
    color: inherit;
    font-size: 16px;
    line-height: 1;
    cursor: pointer;
    transition: background-color 0.15s ease;
}

.expo-navbar__toggle:hover,
.expo-navbar__toggle:focus-visible {
    background-color: rgba(255, 255, 255, 0.12);
}

/* The icon takes the button's white. */
.expo-navbar__toggle i {
    color: inherit;
}

/* The wordmark, at every width. The rail used to carry it on desktop and the bar on phones;
   the reference keeps it in the bar and leaves the rail to the show and the menu. */
.expo-navbar__brand {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
}

/* flex-shrink stays off so a tight header never squeezes the wordmark out of proportion, and
   object-fit means the max-width on narrow screens letterboxes it rather than distorting it.
   32px tall at the asset's 452x144, so just over 100px wide; desktop sizes it by width
   instead - see the min-width block. */
.expo-navbar__brand img {
    display: block;
    height: 32px;
    width: auto;
    object-fit: contain;
}

.expo-navbar__spacer {
    flex: 1 1 auto;
    min-width: 0;
}

/* The app's name at the far end of the bar, where the reference puts "Registration Reports".
   Hidden below 640px, where whoever opened this on a phone knows which app they opened and the
   bar has no room to say it - see the max-width block at the end of the file. */
.expo-navbar__title {
    flex: 0 0 auto;
    margin: 0;
    font-size: 1.25rem;
    font-weight: 600;
    white-space: nowrap;
}

/* ---------- account menu ---------- */

.expo-user-menu {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 8px;
}

/* In the bar the account button is drawn like the toggle beside it: flat, white, a wash on
   hover. Telerik's base theme colour would otherwise put a light grey button on the navy, the
   one solid shape in a bar that is flat everywhere else. The popup is Telerik's own and keeps
   its own surface. */
.expo-navbar .expo-user-menu .k-button {
    background: none;
    border-color: transparent;
    color: #fff;
    box-shadow: none;
    border-radius: 6px;

    /* The toggle's height, not Telerik's. Its medium button is 38px, and inside the bar's
       12px of padding that made the bar 62px rather than the 56 the reference measures - the
       one thing in the bar taller than the 32px logo. */
    height: 32px;
    min-height: 0;
    padding: 6px 10px;
}

.expo-navbar .expo-user-menu .k-button:hover,
.expo-navbar .expo-user-menu .k-button:focus-visible,
.expo-navbar .expo-user-menu .k-button.k-active {
    background-color: rgba(255, 255, 255, 0.12);
    border-color: transparent;
    color: #fff;
}

/* The name takes the colour of whichever surface the button is on: white in the navy bar,
   ink in the light rail. It used to be whitesmoke wherever it went, which is invisible on the
   rail now that the rail is light. */
.expo-user-name {
    font-weight: 600;
    color: inherit;
}

.expo-sidebar .expo-user-name {
    color: var(--kendo-color-on-surface);
}

/* The account button's disclosure caret. Separated from the name and dimmed a little: it is a
   hint that the button opens, not a second thing to read. */
.expo-user-menu__caret {
    margin-left: 6px;
    opacity: 0.75;
}

/* ---------- sidebar ---------- */

/* A column with one scrolling region in it, rather than one scrolling box. Opening a branch
   overflows the menu - an admin with everything open measures well over an 800px viewport -
   and when the sidebar itself was the scroll container the scrollbar came out of the fixed
   width and narrowed every link the instant it appeared; scrollbar-gutter below is the other
   half of that fix.

   Surface Alt with a Border hairline on the right, which is the reference's drawer exactly.
   No shadow: the rail is a region of the page, not a layer over it. On phones, where it does
   become a layer, the max-width block at the end of the file gives it one. */
.expo-sidebar {
    position: fixed;
    top: var(--navbar-height);
    bottom: 0;
    left: 0;
    width: var(--sidebar-width);
    box-sizing: border-box;
    background-color: var(--kendo-color-surface-alt);
    color: var(--kendo-color-on-surface);
    border-right: 1px solid var(--kendo-color-border);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    z-index: 3;
    font-size: 0.875rem;
    font-weight: 400;
}

/* The page beside an open drawer, which is what closes it again on the widths where the drawer
   covers the navbar - see MainLayout, which renders it only while the drawer is open.

   z-index 3 to clear .expo-page, which is position: relative at z-index 2 and comes later in
   the document: at an equal z-index that order decides, so a scrim at 2 lost to it and the taps
   meant for this landed on the page behind. The rail is lifted to 4 in the query below to stay
   over this. The navbar needs no number of its own - it is static at these widths, which keeps
   it out of the contest altogether.

   display: none is the resting state because the button is in the markup at every width; the
   767px query below is the only place it is switched on. */
.expo-sidebar__scrim {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 3;

    /* A fixed overlay stops the page being tapped but not being scrolled: a finger dragged
       across it still scrolls the document underneath, because this is not itself a scroll
       container. These two refuse the gesture here - none for the drag, contain so nothing
       chains past this element - and expoSidebar.lockScroll handles the wheel and the
       scrollbar, which touch-action has no say over. */
    touch-action: none;
    overscroll-behavior: contain;
    border: none;
    padding: 0;
    background-color: rgba(18, 32, 61, 0.45);

    /* Fades in over the rail's own 0.2s slide, so the two read as one movement. */
    animation: expo-scrim-in 0.2s ease;
}

/* The menu's padding and the gap between its blocks are the reference's .nav-menu: 1.25rem
   over 1rem, with 1.5rem between the show card, the section and anything after. */
.expo-sidebar__scroll {
    /* min-height: 0 is what lets this shrink inside the column - a flex item defaults to
       min-height: auto and refuses to go below its content, so without it the region grows to
       fit the open branches and pushes the overflow back onto the sidebar. */
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 1.25rem 1rem;
    display: flex;
    flex-direction: column;
    gap: 1.5rem;

    /* Reserve the gutter whether or not it is in use, so opening a branch does not shift the
       links sideways. Slate Subtle Active for the thumb, which is what the reference themes
       its report list and its grids to. */
    scrollbar-gutter: stable;
    scrollbar-width: thin;
    scrollbar-color: var(--kendo-color-base-subtle-active) transparent;

    /* Scrolling past either end of the menu stops here rather than handing the rest of the
       gesture to the page behind the drawer. */
    overscroll-behavior: contain;
}

.expo-sidebar__scroll::-webkit-scrollbar {
    width: 8px;
}

.expo-sidebar__scroll::-webkit-scrollbar-thumb {
    background-color: var(--kendo-color-base-subtle-active);
    border-radius: 9999px;
}

.expo-sidebar__account,
.expo-sidebar__footer {
    flex: 0 0 auto;
}

/* ---------- the show card ---------- */

/* The reference's compact control card: white on the rail's Surface Alt, a Border Alt
   hairline, an 8px corner and the resting elevation. The show used to be the centrepiece of
   the header; here it has a card of its own at the top of the rail, and nothing in the header
   repeats it. */
.expo-sidebar__card {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    gap: 0.625rem;
    padding: 1rem;
    background-color: var(--kendo-color-surface);
    border: 1px solid var(--kendo-color-border-alt);
    border-radius: 8px;
    box-shadow: var(--kendo-elevation-1);
}

/* The eyebrow: the one place uppercase tracked text is used, and reserved for structural
   labels - "SHOW", "EXPOMOBILE", the role above the menu. 0.75rem at 700 with 0.04em of
   tracking inside a card; the section label below it is a touch larger. */
.expo-sidebar__eyebrow {
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--kendo-color-subtle);
}

/* Matches the nav rows, so the show reads at the same size as the menu under it. */
.expo-sidebar__show-name {
    font-size: 0.875rem;
    font-weight: 600;
    line-height: 1.4;
    color: var(--kendo-color-on-surface);
    overflow-wrap: anywhere;
}

/* An exhibitor's card leads with the exhibitor and keeps the show as a second, quieter line. */
.expo-sidebar__show-sub {
    font-size: 0.8125rem;
    line-height: 1.4;
    color: var(--kendo-color-subtle);
    overflow-wrap: anywhere;
}

/* With no show chosen the card still stands, so the rail keeps its shape, and the line in it
   is the prompt rather than a name. */
.expo-sidebar__show-name--prompt {
    font-weight: 500;
    color: var(--kendo-color-subtle);
}

/* The show id, admins only: the reference's "you are here" pill - Fleet Navy Subtle under
   Fleet Navy, full radius, tabular figures. It used to be orange italic text after a dash in
   the header. */
.expo-sidebar__badge {
    align-self: flex-start;
    display: inline-flex;
    align-items: center;
    min-height: 1.375rem;
    padding: 0 0.5rem;
    background-color: var(--kendo-color-primary-subtle);
    color: var(--kendo-color-primary-on-subtle);
    border-radius: 9999px;
    font-size: 0.6875rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* ---------- section label ---------- */

/* The role, as the section heading over the menu - the reference's "CUSTOM REPORTS" row,
   which is an eyebrow a step larger than the one inside a card and with a little more
   tracking. The 0.625rem under it is the only space between the label and the first row. */
.expo-sidebar__section {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0 0 0.625rem;
    font-size: 0.8125rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--kendo-color-subtle);
}

/* The label and the list are one block; the gap between blocks is the scroll region's. */
.expo-sidebar__menu {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
}

/* ---------- nav rows ---------- */

.expo-nav,
.expo-nav ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

/* 2px between rows, so two lit rows next to each other read as two. */
.expo-nav {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
}

.expo-nav li {
    margin: 0;
}

/* A quiet row: no fill at rest, ink text, 10px over 12px of padding, a 6px corner. Written for
   the three things that render as one - a NavLink, a group's button and a disabled span - which
   is why the font and the border are reset here rather than assumed. */
.expo-nav__link {
    display: flex;
    align-items: center;
    width: 100%;
    gap: 0.625rem;

    /* Required, not cosmetic. A <button> defaults to border-box and an <a> to content-box, so
       without this the anchor items add their padding on top of the width and run past the
       rail. */
    box-sizing: border-box;
    padding: 0.625rem 0.75rem;
    background: none;
    border: none;
    border-radius: 6px;
    color: var(--kendo-color-on-surface);
    text-decoration: none;
    text-align: left;
    font: inherit;
    font-size: 0.875rem;
    line-height: 1.5;
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
}

/* 0.625rem of padding puts these rows at 41px, which is under the 44px a finger needs. The
   menu is the whole point of the drawer, so on a touch pointer the rows grow rather than the
   labels - keyed on the pointer, not the width, because a touch laptop is neither. */
@media (pointer: coarse) {
    .expo-nav__link {
        padding: 0.75rem;
    }
}

/* Hover takes the current page's colours - Fleet Navy Subtle under Fleet Navy - rather than
   the reference's quieter Slate Subtle, so a row lights up the same way it will look once
   chosen. Weight is what still tells the two apart: the current page is 600, a hovered one
   stays at 400, so a hovered row beside the current one never reads as a second current one. */
.expo-nav__link:hover {
    background-color: var(--kendo-color-primary-subtle);
    color: var(--kendo-color-primary-on-subtle);
    text-decoration: none;
}

/* The app's focus ring, Fleet Navy at 2px with 2px of offset; it reads against both the rail
   and a lit row. */
.expo-nav__link:focus-visible {
    outline: 2px solid var(--kendo-color-primary);
    outline-offset: 2px;
}

.expo-nav__link.active,
.expo-nav__link.active:hover {
    background-color: var(--kendo-color-primary-subtle);
    color: var(--kendo-color-primary-on-subtle);
    font-weight: 600;
}

/* Greyed out until a show is chosen - nearly every screen reports on one, and staff sign in
   without one. The hover rule above is written against .expo-nav__link generally and would
   otherwise light these up on the way past, so this puts the resting look back explicitly
   rather than relying on order. Ink at 0.55 over Surface Alt still passes AA. */
.expo-nav__link--disabled,
.expo-nav__link--disabled:hover,
.expo-nav__link--disabled:focus-visible {
    background: none;
    color: var(--kendo-color-on-surface);
    opacity: 0.55;
    cursor: not-allowed;
}

/* The leading icon at the reference's 16px, and never squeezed: inside these flex rows a label
   with no break opportunity would otherwise narrow the icon instead of wrapping, and the icons
   then look inconsistent from row to row. The label absorbs the overflow instead. */
.expo-nav__icon {
    flex: 0 0 16px;
    width: 16px;
    font-size: 14px;
    text-align: center;
    color: inherit;
}

.expo-nav__link > span {
    min-width: 0;
    overflow-wrap: anywhere;
}

/* The chevron on a group, and the external-link mark on the one link that opens a new tab.
   Subtle at rest so it reads as an affordance rather than part of the label. */
.expo-nav__caret {
    margin-left: auto;
    flex: 0 0 auto;
    color: var(--kendo-color-subtle);

    /* Turns in step with its branch's slide - see .expo-nav__branch. */
    transition: transform 0.2s ease;
}

.expo-nav__link.active .expo-nav__caret,
.expo-nav__link:hover .expo-nav__caret {
    color: inherit;
}

.expo-nav__caret--open {
    transform: rotate(90deg);
}

/* Nested levels indent a step. Selector shape matters here: the reset above is `.expo-nav ul`,
   one class plus one element (0,1,1), which out-specifies a bare `.expo-nav__children` (0,1,0)
   - so an unqualified rule loses and every level renders at the same indent. */
.expo-nav ul.expo-nav__children {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    padding-left: 1.25rem;

    /* The branch's single grid row shrinks this to nothing when closed - see below. */
    min-height: 0;
    overflow: hidden;
}

/* The step down from the branch's own link. On the first child rather than as padding on the
   list, which no row can shrink below: a closed branch kept 2px of it under every link. */
.expo-nav ul.expo-nav__children > li:first-child {
    margin-top: 0.125rem;
}

/* A branch slides open and shut rather than appearing. One grid row going from 0fr to 1fr is
   an animation to the children's own height, whatever it is, with nothing measured in script.
   Hidden once closed as well as inert (see NavItem), and visibility switches at the far end
   of the slide each way: visible from the start of opening, hidden only once closing is done. */
.expo-nav__branch {
    display: grid;
    grid-template-rows: 0fr;
    visibility: hidden;
    transition: grid-template-rows 0.2s ease, visibility 0.2s;
}

.expo-nav__branch--open {
    grid-template-rows: 1fr;
    visibility: visible;
}

@media (prefers-reduced-motion: reduce) {
    .expo-nav__branch,
    .expo-nav__caret {
        transition: none;
    }
}

/* ---------- ExpoMobile card ---------- */

/* The QR block as a card like the show's, with its own eyebrow. Inside the scroll region
   rather than pinned below it: pinning cost the menu the block's height permanently, which is
   the space the branches need. */
.expo-sidebar__mobile {
    font-size: 0.8125rem;
    line-height: 1.4;
    color: var(--kendo-color-subtle);
}

.expo-sidebar__mobile p {
    margin: 0;
}

.expo-sidebar__mobile a {
    display: block;
}

.expo-sidebar__mobile img {
    display: block;
    width: 100%;
    border-radius: 6px;
    border: 1px solid var(--kendo-color-border-alt);
    background: #fff;
}

/* ---------- bottom cluster: account, footer ---------- */

/* The account menu's slot on the bottom edge of the rail, on the widths where the navbar has
   given it up. No media query: MainLayout decides, and renders nothing here at the other
   widths. The inset is the scroll region's 1rem, so the button lines up with the rows above
   it, and the hairline separates it from the menu. */
.expo-sidebar__account {
    padding: 0.75rem 1rem;
    border-top: 1px solid var(--kendo-color-border-alt);
}

/* Two hairlines a button apart read as a boxed-off strip rather than a divider, so the footer
   gives its own up whenever the account block is above it - one line now opens the whole
   bottom cluster. The footer keeps the border when it is alone down there. */
.expo-sidebar__account + .expo-sidebar__footer {
    border-top: none;
    padding-top: 0;
}

/* Full width of the rail rather than shrink-to-fit. Sized to the name, the button reads as a
   stray label floating in the drawer, and how long someone's name happens to be decides the
   layout; at full width it reads as the first row of the menu, which is what it now is. */
.expo-sidebar__account .k-button {
    width: 100%;
    justify-content: flex-start;
    border-radius: 6px;
}

/* Telerik puts the button's content inside a .k-button-text span of its own, and that span is
   display: block - so the caret's margin-left: auto below had nothing to push against and sat
   against the name instead of the far edge. This makes that span the flex row the alignment
   needs. min-width: 0 because a flex item will not shrink below its content otherwise, which
   is what lets a long name ellipsise rather than push the caret out of the button. */
.expo-sidebar__account .k-button-text {
    display: flex;
    align-items: center;
    flex: 1 1 auto;
    min-width: 0;
}

.expo-sidebar__account .expo-user-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.expo-sidebar__account .expo-user-menu__caret {
    margin-left: auto;

    /* Clear of the name once that name is truncated, and never squeezed itself. */
    flex: 0 0 auto;
    padding-left: 8px;
}

/* Sits on the bottom edge of the rail, under the scroll region rather than inside it. The
   hairline separates it from the last menu item without drawing a box; subtle because it is a
   notice, not something to read on the way to a link. */
.expo-sidebar__footer {
    padding: 0.75rem 1rem;
    border-top: 1px solid var(--kendo-color-border-alt);
    color: var(--kendo-color-subtle);
    font-size: 0.75rem;
    font-weight: 400;
}

/* --- Page area --- */

.expo-page {
    margin-left: var(--sidebar-width);
    background-color: var(--surface-page);
    border-radius: var(--radius-page) 0 0 0;
    /* One inset on all four sides. The bottom is what separates the footer from the edge of the
       page — it belongs here rather than inside the footer, which has its own background, so
       padding in there only makes the grey bar taller. */
    padding: 15px;

    /* border-box, so the 30px of vertical padding comes out of the min-height rather than being
       added to it. Content-box made every page taller than the space below the navbar, which is
       a scrollbar and a footer just off the bottom of the window on a page with nothing to
       scroll. */
    box-sizing: border-box;
    min-height: calc(100vh - var(--navbar-height));
    position: relative;
    z-index: 2;
    border: none;
}

@supports (height: 70svh) {
    :root {
        --expo-grid-height: 70svh;
    }
}

@media (min-width: 768px) {
    :root {
        --expo-grid-height: 100%;
    }
}

/* Set when a panel is expanded to full width; the sidebar is hidden with it. */
.expo-page--wide {
    margin-left: 0;
}

.expo-page__body {
    background-color: var(--surface-page);
    border-radius: var(--radius-md);
    border: none;

    /* 1px, not 0: it stops the last panel's bottom margin collapsing out through this box and
       landing on the page instead. Worth keeping even now that the last panel has no margin to
       collapse — anything added below one would bring the problem back. */
    padding: 1px;

    margin-bottom: 0;
}

/* Hidden, not deleted. Its text now sits at the bottom of the sidebar instead — see
   .expo-sidebar__footer — and a grey bar across the foot of every page was a lot of furniture
   for a copyright line. The element stays in MainLayout and the rest of this rule stays with
   it, so bringing it back is a one-word change here. */
.expo-footer {
    display: none;

    height: 40px;
    padding: 5px 10px;
    background-color: var(--surface-footer);
    border-radius: var(--radius-md);
    color: var(--text-strong);
    font-style: italic;
    align-items: center;
}

/* The footer sits at the bottom of the window on a short page, and at the end of the content on
   a long one. .expo-page already reserved the height — min-height is a viewport minus the
   navbar — but the footer is simply the last child in flow, so a page with little on it left
   the grey bar stranded partway up with 550-odd pixels of blank page under it.

   margin-top: auto rather than a fixed or sticky footer: this is a copyright line, and pinning
   one to the glass would spend 40px of every screen, on every report, forever. An auto margin
   in a flex column eats whatever space is free and collapses to nothing when there is none, so
   a long page is left exactly as it was.

   Every width, not just desktop: .expo-page reserves a viewport's height at any size, so the
   stranded footer is the same bug on a phone — and short pages are commoner there, not rarer,
   since a one-column layout leaves plenty of screens with nothing below the fold. The only
   mobile rules touching .expo-page set margin-left and border-radius, neither of which cares
   whether the box lays out as block or flex. */
.expo-page {
    display: flex;
    flex-direction: column;
}

.expo-page > .expo-footer {
    margin-top: auto;
}

/* ==========================================================================
   Panels — the collapsible report chrome from _panelHeader.cshtml
   ========================================================================== */

.expo-panel {
    background: var(--surface-page);
    border: 1px solid var(--border-muted);
    border-radius: var(--radius-sm);
    margin-bottom: 16px;
}

/* The gap belongs between panels, so the last one drops it — otherwise every page ends on 16px
   of nothing above its own bottom padding.

   "No later sibling panel", not :last-child: a page is free to put something after its panels —
   a note, a <style> block — and :last-child would then match nothing and leave the gap behind.
   :last-of-type is no better, since these are divs and so is half of what sits beside them.
   :has() is already load-bearing elsewhere in this file, so it is not a new dependency. */
.expo-panel:not(:has(~ .expo-panel)) {
    margin-bottom: 0;
}

.expo-panel__header {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    background: var(--surface-muted);
    border-bottom: 1px solid var(--border-muted);
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    flex-wrap: wrap;
}

.expo-panel__title {
    display: flex;
    align-items: center;
    gap: 8px;
    font-weight: 600;
    color: var(--text-strong);
    background: none;
    border: none;
    cursor: pointer;
    font-size: 16px;
    padding: 0;
    font-family: inherit;
}


.expo-panel__body {
    padding: 12px;
    overflow-x: auto;
}

/* --- Exhibitor panels ---------------------------------------------------- */

/* The exhibitor's screens carry the chrome the report grids and dashboard cards already use:
   a white surface, a hairline in --kendo-color-border-alt, the 8px radius and elevation-1 —
   in place of the 4px box and grey title strip the panels inherited from the legacy app, which
   are painted with the old --surface-muted / --border-muted pair.

   Scoped by a modifier on .expo-page rather than applied to .expo-panel outright, because the
   ask was the exhibitor's surfaces; dropping the scope promotes it to the whole app.

   Placement matters. These sit above the :has() rules further down that dissolve a panel
   wrapping a grid, so those still win and a grid panel stays one surface rather than a frame
   inside a frame. Same specificity, later rule, deliberately. */
.expo-page--exhibitor .expo-panel {
    background: var(--kendo-color-surface);
    border: 1px solid var(--kendo-color-border-alt);
    border-radius: var(--kendo-border-radius-lg);
    box-shadow: var(--kendo-elevation-1);
}

.expo-page--exhibitor .expo-panel__header {
    background: transparent;
    border-bottom: 1px solid var(--kendo-color-border-alt);
    border-radius: var(--kendo-border-radius-lg) var(--kendo-border-radius-lg) 0 0;
    padding: 12px 16px;
}

.expo-page--exhibitor .expo-panel__title {
    color: var(--kendo-color-on-surface);
}


.expo-page--exhibitor .expo-panel__body {
    padding: 16px;
}

/* ==========================================================================
   Full-height grid pages — Panel Fill
   ========================================================================== */

/* A page whose whole body is one Fill panel becomes a fixed-height column: the panel takes
   whatever the title and the footer leave, and the grid inside shrinks to fit rather than
   growing with its rows and pushing the footer below the fold.

   The definite height at the top of this chain is the whole point. min-height leaves the height
   auto, and a flex item cannot shrink inside an auto-height container — which is why the grid
   used to run past the bottom of the window however the panel below it was styled.

   Only pages with a single panel opt in, because the page stops scrolling: a second panel
   underneath would have nowhere to go.

   The selector reaches all the way to .expo-panel__body rather than stopping at the panel, so a
   panel the user has collapsed leaves the page scrolling normally — there is no body to hand the
   space to. It has to be written as one flat chain: :has() inside :has() is invalid, and an
   invalid selector takes its whole rule with it silently. */
@media (min-width: 768px) {
    .expo-page:has(> .expo-page__body > .expo-panel--fill > .expo-panel__body) {
        display: flex;
        flex-direction: column;

        height: calc(100vh - var(--navbar-height));
    }

    /* dvh where it exists: a mobile browser counts its collapsing address bar inside 100vh, so
       at rest the footer sat just below the fold. This breakpoint is mostly past that, but a
       short landscape window hits it too. */
    @supports (height: 100dvh) {
        .expo-page:has(> .expo-page__body > .expo-panel--fill > .expo-panel__body) {
            height: calc(100dvh - var(--navbar-height));
        }
    }

    /* The title, the masquerade banner and the footer keep their natural height; only the body
       gives or takes. flex-shrink defaults to 1, so without this the footer is the first thing
       to be squeezed. */
    .expo-page:has(> .expo-page__body > .expo-panel--fill > .expo-panel__body) > .expo-alerts,
    .expo-page:has(> .expo-page__body > .expo-panel--fill > .expo-panel__body) > .expo-footer {
        flex: none;
    }

    /* min-height: 0 on every link in the chain. A flex item's default minimum size is its
       content, so one omission anywhere here and the whole thing stops shrinking. */
    .expo-page:has(> .expo-page__body > .expo-panel--fill > .expo-panel__body) > .expo-page__body {
        flex: 1 1 auto;
        min-height: 0;
        display: flex;
        flex-direction: column;
    }

    .expo-panel--fill {
        flex: 1 1 auto;
        min-height: 0;
        display: flex;
        flex-direction: column;
    }

    .expo-panel--fill > .expo-panel__header {
        flex: none;
    }


    .expo-panel--fill > .expo-panel__body {
        flex: 1 1 auto;
        min-height: 0;
        display: flex;
        flex-direction: column;

        /* The panel body is normally overflow-x: auto, which the spec turns into a scroll
           container on both axes. Inside the chain the grid owns the scrolling, and a second
           scrollbar around it just moves the header band out of view. */
        overflow: hidden;
    }

    /* Everything else in the body — a session picker, a message line, the swap buttons — keeps
       its own height. This has to come first: it is the same specificity as the rule below, so
       source order is what decides which one the grid gets. */
    .expo-panel--fill > .expo-panel__body > * {
        flex: none;
    }

    /* The grid is the piece that gives up the space. Two shapes reach here: a typed grid sits
       directly in the body, and a dynamic report grid is wrapped in its own shell. */
    .expo-panel--fill > .expo-panel__body > .k-grid,
    .expo-panel--fill > .expo-panel__body > .expo-grid-shell {
        flex: 1 1 auto;
        min-height: 0;
    }

    /* The shell is sized by flex, so the grid inside it takes the shell's height. ReportGrid
       passes Height="100%" for this; a fixed calc() there would ignore the space it was given. */
    .expo-panel--fill > .expo-panel__body > .expo-grid-shell > .k-grid {
        height: 100%;
    }

    /* A grid told to size to its rows opts out of the two rules above: it keeps its own height
       and the shell around it stops stretching, or the frame stands full height with the data
       at the top and nothing under it. Its own height goes back to auto for the same reason —
       100% of a shell now sized by its content is circular.

       Check-In Total is one row and Check-In by Date Time is one row per show day, so both ask
       for this; every other report fills the panel as before. */
    .expo-panel--fill > .expo-panel__body > .expo-grid-shell--auto {
        flex: none;
    }

    .expo-panel--fill > .expo-panel__body > .expo-grid-shell--auto > .k-grid {
        height: auto;
    }

    /* Which leaves the panel to scroll if the rows outrun it. The body is overflow: hidden
       above, because normally the grid owns the scrolling — an auto-height grid does not, so a
       long show would simply be cut off at the bottom of a page that cannot scroll. */
    .expo-panel--fill > .expo-panel__body:has(> .expo-grid-shell--auto) {
        overflow-y: auto;
    }

    /* And a chart sharing the panel takes whatever the grid gave up — see Check-In Total. */
    .expo-panel--fill > .expo-panel__body:has(> .expo-chart-shell) > .expo-chart-shell {
        flex: 1 1 auto;
        min-height: 0;
    }

    /* A panel whose grid has no rows yet renders a loader or an empty line instead. Centring it
       in the space the grid would have had beats leaving it pinned under the header. */
    .expo-panel--fill > .expo-panel__body > .expo-empty,
    .expo-panel--fill > .expo-panel__body > .expo-loader {
        flex: 1 1 auto;
        min-height: 0;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
    }
}

/* Maximised panel takes over the viewport, as .panel-fullscreen did. */
/* The "All Shows / +/- 31 Days" style toggle that sits inside a panel header */
.expo-radio-card {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex-wrap: nowrap;
    padding: 2px 8px;
    border: 1px solid var(--ats-orange-dark);
    border-radius: 10px;
    background-color: var(--ats-orange-dark);
    color: #fff;
    font-size: 14px;
    font-weight: 500;
}

.expo-radio-card label {
    margin: 0;
    font-weight: 500;
    cursor: pointer;
}

/* ==========================================================================
   Dashboard cards
   ========================================================================== */

.expo-dash-grid {
    display: grid;
    /* 320, not 300: two figures and the icon chip need the extra room before wrapping. */
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: 16px;
    margin-bottom: 16px;
}

/* The card wears the same surface the report grids do — white, hairline border,
   elevation-1, lg radius — so the dashboard and the report pages read as one product
   rather than two. The orange it used to be filled with now sits only in the icon chip,
   which is the one place it can go: #f18a00 behind white text is 2.5:1, which fails AA
   at every size these cards use. */
.expo-dash-card {
    display: flex;
    align-items: center;
    gap: 18px;
    padding: 18px 20px;
    /* A <button> is border-box by default and a <div> is not, and this rule paints both —
       without this the static exhibitor card renders 42px wider than the clickable ones and
       overflows the grid on a phone. */
    box-sizing: border-box;
    background-color: var(--kendo-color-surface);
    color: var(--kendo-color-on-surface);
    border: 1px solid var(--kendo-color-border-alt);
    border-radius: var(--kendo-border-radius-lg);
    box-shadow: var(--kendo-elevation-1);
    cursor: pointer;
    text-align: left;
    font: inherit;
    width: 100%;
    transition: box-shadow 0.16s cubic-bezier(0.2, 0.8, 0.2, 1),
                transform 0.16s cubic-bezier(0.2, 0.8, 0.2, 1),
                border-color 0.16s ease;
}

.expo-dash-card:hover {
    box-shadow: var(--kendo-elevation-2);
    border-color: var(--kendo-color-tertiary);
    transform: translateY(-1px);
}

.expo-dash-card:focus-visible {
    outline: 2px solid var(--kendo-color-tertiary);
    outline-offset: 2px;
}

.expo-dash-card:active {
    transform: translateY(0);
    box-shadow: var(--kendo-elevation-1);
}

/* Exhibitor-facing cards do not link through to staff-only screens, so they render as a
   <div> rather than a disabled <button> — see DashCard.razor. Nothing should suggest they
   can be pressed. */
.expo-dash-card--static {
    cursor: default;
}

.expo-dash-card--static:hover {
    box-shadow: var(--kendo-elevation-1);
    border-color: var(--kendo-color-border-alt);
    transform: none;
}

.expo-dash-card__icon {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 52px;
    height: 52px;
    border-radius: var(--kendo-border-radius-lg);
    background-color: rgba(241, 138, 0, 0.12);
    color: var(--ats-orange-dark);
    font-size: 22px;
    line-height: 1;
}

.expo-dash-card__stats {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    /* No gap: the space between two figures is owned by the divider rule below, so that the
       hairline sits centred in it rather than off to one side. */
    gap: 0;
    margin-left: auto;
    min-width: 0;
}

.expo-dash-card__stat {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    /* Tight enough that a figure and its caption read as one unit. Two figures sitting side
       by side at the same size and weight blur into a single number otherwise, and the fix
       is proximity first — bind each number to its own label, then separate the pairs. */
    gap: 2px;
    min-width: 0;
}

.expo-dash-card__stat + .expo-dash-card__stat {
    margin-left: 22px;
    padding-left: 22px;
    border-left: 1px solid var(--kendo-color-border-alt);
}

/* Once the figures stack, a vertical rule would be a line hanging off the side of nothing,
   so it lies down with them. */
@media (max-width: 767px) {
    /* Stacked outright here rather than left to wrap when they happen not to fit: beside the
       icon chip there is little width to share, and two figures squeezed side by side on a
       phone read as one number. The rules below already assume this arrangement. */
    .expo-dash-card__stats {
        flex-direction: column;
    }

    .expo-dash-card__stat + .expo-dash-card__stat {
        margin-left: 0;
        margin-top: 12px;
        padding-left: 0;
        padding-top: 12px;
        border-left: none;
        border-top: 1px solid var(--kendo-color-border-alt);
        align-self: stretch;
    }
}

.expo-dash-card__figure {
    font-size: 1.75rem;
    font-weight: 700;
    line-height: 1.1;
    /* The same treatment the grid cells get. These figures reload every 60 seconds and
       fixed-width digits keep them from jittering as the counts change. */
    font-variant-numeric: tabular-nums;
    overflow-wrap: anywhere;
}

.expo-dash-card__label {
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--kendo-color-subtle);
    white-space: nowrap;
}

/* ==========================================================================
   Statistics tiles (Lead Statistics)
   ========================================================================== */

/* Stretched rather than top-aligned, so the two cards side by side end on the same line
   instead of the shorter one leaving a block of page under itself. */
.expo-stat-sections {
    display: flex;
    align-items: stretch;
    gap: 20px;
    flex-wrap: wrap;
}

/* Lead Statistics' two cards: the figures, taking most of the room, and the rental devices
   beside them. Side by side the figures take three parts of the spare width to the devices'
   one; on a screen too narrow for both - 1280 and below - the devices wrap under the figures
   and grow to the full row, rather than sitting at 440px with the rest of the line empty.
   Doubled up to outrank .expo-stat-section's own flex, which comes later in the file. */
.expo-stat-section.expo-stat-section--figures {
    flex: 3 1 640px;
}

.expo-stat-section.expo-stat-section--devices {
    flex: 1 1 440px;
}

/* One group of figures within the card - its heading and its row of tiles. */
.expo-stat-group + .expo-stat-group {
    margin-top: 24px;
}

.expo-stat-section {
    background: var(--surface-raised);
    padding: 20px 25px;
    border-radius: 10px;
    margin-bottom: 20px;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);
    flex: 1 1 720px;
    min-width: 0;
}

/* The Show List's search block. It keeps .expo-stat-section only for the width cap and the
   heading's margin; every piece of the card look is taken back off, because this is the one
   thing on its page and a box drawn around the only object on a screen frames nothing. */
.expo-stat-section--find {
    box-sizing: border-box;

    /* Both, and the width is not redundant: an auto margin on the cross axis cancels a flex
       item's stretch, so inside .expo-page's column this would otherwise shrink to the width of
       its own text — about 265px — and the cap would never come into play. */
    width: 100%;
    max-width: 600px;

    background: none;
    box-shadow: none;
    border-radius: 0;
    padding: 0;

    /* Auto side margins centre it, including as a flex item — .expo-page is a column flex
       container since the footer change, and auto margins absorb free space there too. */
    margin-inline: auto;
}

/* Padding rather than margin: h1-h4 carry margin-top: 0 globally, and a margin here would also
   be free to collapse out through the section now that it has none of its own. */
.expo-stat-section--find h2 {
    padding-top: 15px;
}

/* Three equal columns across the card rather than three fixed 220px tiles, which on a wide
   screen filled half the card and left the rest empty. */
.expo-stat-row {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 12px;
}

/* Same move as the dashboard cards, with one difference: these sit inside
   .expo-stat-section, which is already an elevated white card. A second elevated white
   card inside it would be a box in a box, so the tile reads as a recess in its section —
   subtle fill, hairline, no shadow of its own. */
/* The label sits under its figure rather than pinned to the foot of a 120px tile, which held
   the two apart with a gap that was most of the tile. */
.expo-stat-tile {
    min-width: 0;
    padding: 16px 20px;
    border-radius: var(--kendo-border-radius-lg);
    background-color: var(--kendo-color-base-subtle);
    color: var(--kendo-color-on-surface);
    border: 1px solid var(--kendo-color-border-alt);
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* What the figure was worked out from - see StatTile's Note. Body text, small, so it reads as
   a footnote to the label rather than a second one. */
.expo-stat-tile__note {
    font-size: 0.8125rem;
    color: var(--kendo-color-subtle);
}

/* A figure that cannot be right as it stands. The warning pair, so it is defined for both
   themes and clears contrast, as the labels' tints do - see .expo-label--warning. */
.expo-stat-tile--warning {
    background-color: var(--kendo-color-warning-subtle);
    border-color: var(--kendo-color-warning);
}

.expo-stat-tile--warning .expo-stat-tile__value,
.expo-stat-tile--warning .expo-stat-tile__note {
    color: var(--kendo-color-warning-on-subtle);
}

.expo-stat-tile__value {
    font-size: 2.5rem;
    font-weight: 700;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}

.expo-stat-tile__label {
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--kendo-color-subtle);
}

/* Lead Statistics' editable total: a tile like .expo-stat-tile - the same recess, hairline and
   uppercase label - laid out as a row, with the input where a tile has its value. */
.expo-stat-field {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 14px 18px;
    border-radius: var(--kendo-border-radius-lg);
    background-color: var(--kendo-color-base-subtle);
    border: 1px solid var(--kendo-color-border-alt);
}

.expo-stat-field__label {
    margin: 0;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--kendo-color-subtle);
}

/* The number is the figure the tile is about, so it is set like one: bold, at the tiles'
   value weight if not their size - a 40px input would be all the section was. */
.expo-stat-field .k-input-inner {
    font-size: 1.25rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* The box keeps the width its number needs - see .expo-autosize-number. As a flex item it
   would otherwise give way to the label beside it and cut "1010" down to "10…". */
.expo-stat-field .k-numerictextbox {
    flex: none;
}

.expo-stat-field__saved {
    margin: 10px 0 0;
}

/* Dashboard - the Location Venue rename. The result line sits above the grid it reports on,
   and the dialog is a sentence, a labelled box and a warning. */
.expo-venue-rename__message {
    display: inline-block;
    margin: 0 0 10px;
}

.expo-venue-rename {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.expo-venue-rename p {
    margin: 0;
}

.expo-venue-rename label {
    font-weight: 600;
}

.expo-venue-rename__warning {
    color: var(--text-muted);
    font-size: 0.875rem;
}

/* Lead Statistics' rental devices, drawn as the app's grids are: the frame of .k-grid, the
   navy header band of .k-grid-header, and its body - one surface, hairlines between rows, no
   striping - so a table of three rows reads as one more grid rather than as a leftover from
   the legacy page's grey Bootstrap tables. */
.expo-stat-table {
    width: 100%;
    margin-top: 1rem;
    border-collapse: separate;
    border-spacing: 0;
    font-size: 0.875rem;
    border: 1px solid var(--kendo-color-border-alt);
    border-radius: var(--kendo-border-radius-lg);
    box-shadow: var(--kendo-elevation-1);
    overflow: hidden;
    background-color: var(--kendo-color-surface-alt);
}

.expo-stat-table thead th {
    padding: 10px 12px;
    text-align: left;
    font-weight: 700;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--kendo-color-on-primary);
    background-color: var(--kendo-color-primary);
}

.expo-stat-table tbody th,
.expo-stat-table tbody td {
    padding: 10px 12px;
    text-align: left;
    font-weight: 400;
    color: var(--kendo-color-on-surface);
    border-top: 1px solid var(--kendo-color-border);
}

.expo-stat-table tbody tr:first-child > * {
    border-top: none;
}

/* Counts sit on their right edge, as every number column in the grids does. Only the counts
   are held to one line; a heading wraps, since "Total Number of Units Ordered" on one line
   pushed the table out past its card. */
.expo-stat-table .expo-stat-table__number {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.expo-stat-table td.expo-stat-table__number {
    white-space: nowrap;
}

/* A heading short enough to keep whole: "% of Total" broken over two lines reads as two
   separate words. The table takes the width from the long heading beside it, which still
   wraps. */
.expo-stat-table .expo-stat-table__whole {
    white-space: nowrap;
}

/* A label / value list - Settings' account and show details. Deliberately plain: no frame, no
   fill, no band, only a hairline between rows so the eye can follow a label across to its
   value. Labels are the smaller, muted text, values the page's own body text, which keeps the
   values - the part anyone came to read - as the strongest thing in each row.

   The label column is one fixed width in every list, so the values start on the same line
   down the whole page rather than wherever each list's own longest label ends - 168px holds
   "Registration data", the longest label Settings has, with its 32px gap. A longer label still
   fits, since a label does not wrap; it just pushes its own list's values over. A long value,
   such as an email address with two recipients in it, wraps within its own column instead of
   under the labels. */
/* 640px rather than 560: the fixed label column takes 168px of it, and 560 left the values
   392px - short of the 410px an account email naming two recipients needs on one line. */
.expo-detail-table {
    width: 100%;
    max-width: 640px;
    border-collapse: collapse;
    font-variant-numeric: tabular-nums;
}

.expo-detail-table th {
    width: 168px;
    box-sizing: border-box;
    white-space: nowrap;
    padding: 10px 32px 10px 0;
    text-align: left;
    vertical-align: baseline;
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--kendo-color-subtle);
}

.expo-detail-table td {
    padding: 10px 0;
    vertical-align: baseline;
    color: var(--kendo-color-on-app-surface);
    overflow-wrap: anywhere;
}

.expo-detail-table tr:not(:last-child) > * {
    border-bottom: 1px solid var(--kendo-color-border-alt);
}

.expo-stat-input {
    width: 60px;
    text-align: center;
    border: 1px solid var(--ats-blue);
    border-radius: var(--radius-md);
    background-color: var(--surface-raised);
    color: inherit;
    font-weight: bold;
}

/* ==========================================================================
   Loading indicator — the three bouncing dots from Site.css
   ========================================================================== */

.expo-loader {
    padding: 15px;
}

.expo-loader__dots {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 40px;
}

.expo-loader__dots > span {
    width: 15px;
    height: 15px;
    display: block;
    background: lightgrey;
    border-radius: 50%;
    position: relative;
    margin: 0 5px;
    animation: expo-bounce 1s linear infinite;
}

.expo-loader__dots > span:nth-child(2) { animation-delay: 0.25s; }
.expo-loader__dots > span:nth-child(3) { animation-delay: 0.5s; }

.expo-loader__message {
    text-align: center;
    /* The CSS keyword grey is 3.95:1 on white and fails AA; --text-muted is the token for
       exactly this and clears it at 4.83:1. */
    color: var(--text-muted);
    padding: 10px;
    font-size: 20px;
    font-weight: 500;
}

@keyframes expo-bounce {
    0%   { top: 0; }
    50%  { top: 15px; background-color: #000; }
    100% { top: 0; }
}

/* Stands in for a figure that has not loaded yet. Sized in ch and em so it occupies about
   the space the number will, which keeps the card from reflowing when the count lands —
   the same reason the grid's empty state reuses the grid's own frame. */
.expo-skeleton {
    display: inline-block;
    width: 4.5ch;
    height: 0.8em;
    border-radius: var(--kendo-border-radius-md);
    background-color: var(--kendo-color-base-subtle);
    animation: expo-skeleton 1.4s ease-in-out infinite;
}

@keyframes expo-skeleton {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.45; }
}

@media (prefers-reduced-motion: reduce) {
    .expo-loader__dots > span {
        animation: none;
    }

    .expo-skeleton {
        animation: none;
    }

    .expo-dash-card {
        transition: none;
    }

    .expo-dash-card:hover {
        transform: none;
    }
}

.expo-empty {
    text-align: center;
    padding: 24px;
    color: var(--text-muted);
}

/* A message standing in for a grid inside an expanded row starts where the row starts.

   A detail cell spans every column of the grid above it, and on Edit Attendance that is the
   whole badge layout — 7,875px of cell. .expo-empty centres its text, so the message was laid
   out around x=4,000: in the DOM, correct, and thousands of pixels right of anything on screen.
   It read as an expanded row with nothing in it.

   Sizing the box to its text and aligning left puts it at the start of the cell, which is where
   the grid sits until someone scrolls. Sticky would be better — it would hold the message in
   view at any scroll offset — but .k-detail-cell is overflow:hidden, and that makes the cell
   the sticky element's scroll container rather than the grid, so it never moves. Changing that
   overflow is not worth it for a message. */
.k-detail-cell > .expo-empty,
.k-detail-cell > .expo-loader {
    width: max-content;
    max-width: 100%;
    text-align: left;
}

/* The message sits in the middle of the space it is given, rather than 16px above it.
   Telerik's `.k-body p` sets `margin: 0 0 var(--kendo-font-size)`, and at (0,1,1) it
   out-specifies a bare `.expo-empty` at (0,1,0) — so every empty state carried a stray 16px
   below the text that the 24px padding above it had no answer to. In a panel that reads as
   slightly loose spacing; in the detail row an expanded Exhibitor List opens, where the cell is
   only as tall as this one line, it tips the text visibly off centre: 11px above, 28px below.

   Qualified enough to win the specificity argument rather than reaching for !important, which
   would have to be undone by the next rule that needs to space one of these. */
.k-body p.expo-empty {
    margin-bottom: 0;
}

/* ==========================================================================
   Login / slim shell
   ========================================================================== */

/* The slim shell has no navbar, so it owns the whole viewport: a light ground with the
   colour logo above the card. */
.expo-slim-page {
    background-color: var(--expo-slim-ground);
    padding: 40px 15px;

    /* A definite height, not min-height. Under min-height the box is auto-height and simply
       grows to fit, so free space is never negative and the spacer below can never shrink —
       which is what made a short window scroll. Owning the scroll here also means the light
       ground always covers the viewport instead of ending where the content does.
       dvh so a mobile browser's collapsing toolbar is not counted twice. */
    height: 100vh;
    height: 100dvh;
    overflow-y: auto;

    /* Centres the logo and card in the viewport, both axes. "safe" keeps a card taller than
       the window from being centred into an unreachable overflow above the scroll origin;
       browsers without it fall back to plain centre, which is fine at these card heights. */
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: safe center;
    box-sizing: border-box;
}

/* Centring the logo-plus-card group leaves the card itself sitting low, because the logo and
   its gap only occupy the top. This spacer balances them below, lifting the group by half its
   height so the card lands on the optical centre.
   It is a shrinkable flex item rather than bottom padding on purpose: padding is real layout
   height, and on a short window (iPhone SE is 375x667) it made the page scroll even though the
   content itself fits. This gives up its height instead, so the offset is a luxury the layout
   only takes when there is room. */
.expo-slim-page::after {
    content: "";
    flex: 0 1 calc(var(--slim-logo-height) + var(--slim-brand-gap));
}

/* The spacer above is the only thing allowed to absorb a short window; without this the card
   and logo would shrink first. */
.expo-slim-brand,
.expo-login {
    flex: none;
}

.expo-slim-brand {
    text-align: center;
    margin-bottom: var(--slim-brand-gap);
}

/* The height is explicit rather than derived from the image, because the shell's offset above
   is calculated from it; object-fit keeps the artwork undistorted if the width ever caps. */
.expo-slim-brand__logo {
    /* block, not inline-block: an inline image sits on the text baseline and adds a few pixels
       of descender space below it, which would throw off the offset calculated above. */
    display: block;
    margin: 0 auto;
    width: var(--slim-logo-width);
    height: var(--slim-logo-height);
    max-width: 100%;
    object-fit: contain;
}

/* Two logos in the markup, one shown: the colour one's navy wordmark is lost on the dark
   ground, so the bar's white version stands in for it there. ATSHub's rule. */
.expo-slim-brand__logo--dark {
    display: none;
}

:root[data-theme="dark"] .expo-slim-brand__logo--light {
    display: none;
}

:root[data-theme="dark"] .expo-slim-brand__logo--dark {
    display: block;
}

.expo-login {
    /* align-items: center on the shell would otherwise shrink this to its content. */
    width: 100%;
    max-width: 470px;
    margin: 0 auto;
}

.expo-login__card {
    background: var(--expo-login-card);
    border-radius: 10px;
    box-shadow: var(--expo-login-card-shadow);
    overflow: hidden;
}

.expo-login__body {
    padding: 18px;
}

/* A filled navy block inset inside the card rather than a flush card header — the white gutter
   around it is what makes it read as a plate rather than a stripe. */
.expo-login__header {
    margin: 0 0 18px;
    padding: 22px 16px;
    background-color: var(--ats-navy);
    color: #fff;
    border-radius: 10px;
    text-align: center;
    font-size: 28px;
    font-weight: 700;
}

.expo-login__field {
    margin-bottom: 14px;
}

.expo-login__field input {
    width: 100%;
    padding: 14px 16px;
    font: inherit;
    font-size: 15px;
    color: var(--expo-ink-navy);
    background-color: var(--expo-login-field-bg);
    border: 1px solid var(--expo-login-field-border);
    border-radius: 10px;
    box-sizing: border-box;
}

.expo-login__field input::placeholder {
    color: var(--expo-ink-navy);
    opacity: 0.65;
}

.expo-login__field input:focus {
    outline: 2px solid var(--expo-ink-navy);
    outline-offset: 1px;
    border-color: var(--expo-ink-navy);
}

/* Sized to its text and centred, with the orange keyline that is the one accent on this screen. */
.expo-login__button {
    display: block;
    margin: 22px auto 0;
    padding: 10px 30px;
    background-color: var(--ats-navy);
    border: 3px solid var(--ats-orange);
    color: #fff;
    font-size: 17px;
    font-weight: 700;
    border-radius: 10px;
    cursor: pointer;
}

.expo-login__button {
    transition: filter 0.16s ease, box-shadow 0.16s ease;
}

.expo-login__button:hover {
    filter: brightness(1.15);
}

.expo-login__button:active {
    filter: brightness(1.05);
}

@media (prefers-reduced-motion: reduce) {
    .expo-login__button {
        transition: none;
    }
}

.expo-login__forgot {
    display: block;
    text-align: center;
    color: var(--expo-ink-navy);
    font-size: 14px;
    text-decoration: none;
}

.expo-login__forgot:hover {
    text-decoration: underline;
}

/* Login only. That page puts the link inside the form, directly under the Login button, where
   it needs separating from it. The reminder pages stack theirs in the footer instead, and a top
   margin there would push the first link away from the card rather than off its neighbour. */
.expo-login form .expo-login__forgot {
    margin-top: 16px;
}

/* The reminder pages stack two links in the footer. Only the second one is spaced, so the pair
   stays together as a block under the card instead of drifting down it. */
.expo-login__footer .expo-login__forgot + .expo-login__forgot {
    margin-top: 10px;
}

.expo-login__footer {
    padding: 10px 15px;
    text-align: center;
    background: var(--surface-footer);
    border-top: none;
}

.expo-label {
    display: inline-block;
    padding: 4px 8px;
    border-radius: var(--radius-sm);
    font-size: 13px;
    white-space: normal;
}

/* Tinted fills rather than the Bootstrap 3 solids these were. White on those measured
   3.96:1 (danger), 2.48:1 (success) and 2.09:1 (info) — and info is what carries the banner
   telling an admin they are operating inside someone else's account, which is the one message
   in the app that must not be hard to read. The subtle/on-subtle pairs clear 7:1 and are
   already defined for both themes, so these follow dark mode the day it is wired. */
.expo-label--danger {
    background: var(--kendo-color-error-subtle);
    color: var(--kendo-color-error-on-subtle);
    border: 1px solid var(--kendo-color-error);
}

.expo-label--success {
    background: var(--kendo-color-success-subtle);
    color: var(--kendo-color-success-on-subtle);
    border: 1px solid var(--kendo-color-success);
}

.expo-label--info {
    background: var(--kendo-color-info-subtle);
    color: var(--kendo-color-info-on-subtle);
    border: 1px solid var(--kendo-color-info);
}

/* Completes the set. The other three came from the legacy app; this one exists because the
   exhibitor's unclaimed app codes need a "waiting on you" tone that success and danger do not
   carry, and the warning pair was already defined for both themes. */
.expo-label--warning {
    background: var(--kendo-color-warning-subtle);
    color: var(--kendo-color-warning-on-subtle);
    border: 1px solid var(--kendo-color-warning);
}

/* --- Attendance Swap ----------------------------------------------------- */

/* How many scans are ticked, at the right of a session's scan toolbar. */
.expo-swap-count {
    color: var(--text-muted);
    font-size: 13px;
}

/* --- Exhibitor devices --------------------------------------------------- */

.expo-device-row {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.expo-device-empty {
    margin: 0;
    color: var(--text-muted);
}

/* A device's qualifier pie and grid: side by side on a desktop, as the legacy's two
   col-md-6 halves were, stacked below. Top-aligned so the grid keeps its own height. */
.expo-device-qualifiers {
    display: grid;
    gap: 16px;
    align-items: start;
}

@media (min-width: 992px) {
    .expo-device-qualifiers {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    }
}

/* An unassigned app code: a label that opens its QR / email dialog. */
.expo-device-code {
    font: inherit;
    font-size: 13px;
    cursor: pointer;
}

.expo-device-code:hover {
    border-color: var(--kendo-color-warning-on-subtle);
}

.expo-device-code:focus-visible {
    outline: 2px solid var(--kendo-color-primary);
    outline-offset: 2px;
}

/* The app code dialog: the code over its QR code, then the email row. */
.expo-app-code {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 12px;
    text-align: center;
}

.expo-app-code__value {
    margin: 0;
    font-size: 1.25rem;
    font-weight: 700;
    letter-spacing: 0.05em;
}

.expo-app-code img {
    background: #fff;
    padding: 6px;
}

.expo-app-code__send {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
}

/* Wide enough for a long address (Joshua.Bullard@american-tradeshow.com, 37 characters) with
   room to spare; the dialog has no set width, so it grows to fit. The extra em covers the
   input's padding and border. */
.expo-app-code__send .k-textbox {
    flex: 0 1 auto;
    width: calc(43ch + 1em);
    min-width: 0;
}

.expo-app-code .expo-label {
    margin: 0;
}

/* ==========================================================================
   Utilities
   ========================================================================== */

.expo-alerts {
    position: sticky;
    top: 0;
    z-index: 1000;
    margin-bottom: 8px;
}

.expo-toolbar {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-bottom: 12px;
}

.expo-toolbar__spacer {
    margin-left: auto;
}

/* A grid whose page has put up its loading animation by hand - Attendees List while a search
   runs, which Telerik does not animate, being a read started from code. The loader is the
   grid's own: Telerik keeps it in the data area at all times and hides it with an inline
   display: none, so this only overrides that, and the search shows the same overlay and
   spinner, in the same place, as a filter or a sort. The toolbar is outside it, so the search
   box stays usable.

   On the panel rather than the grid's own Class, because Telerik does not re-render the grid
   while it is waiting on OnRead - a class set then arrives only once the rows have. The panel
   is the page's own markup, and a wrapper between it and the grid would undo the panel rules
   that look for the grid as its body's child. */
.expo-panel.expo-grid-busy .k-grid > .k-grid-aria-root > .k-grid-container > .k-loader-container {
    display: flex !important;
}

/* GridToolbar's search box, inside the span that hears its keystrokes. The span takes no box
   of its own, so the search box is still the toolbar's flex item. */
.expo-grid-toolbar__search {
    display: contents;
}

/* A numeric box as wide as the number in it, re-measured on every keystroke - Lead Statistics'
   Total Number of Exhibiting Companies. field-sizing does the measuring, so it follows the
   text as it is typed rather than the value once Telerik has debounced it; it needs the
   input's own width left auto, which Telerik sets to fill the wrapper. The floor keeps an
   empty or one-digit box from shrinking to a sliver beside its spinner. */
.k-numerictextbox.expo-autosize-number {
    width: auto;
}

.expo-autosize-number .k-input-inner {
    field-sizing: content;
    width: auto;
    min-width: 3ch;
}

/* Browsers without field-sizing - Firefox, as of writing - get a fixed box wide enough for any
   count a show could have: seven digits. */
@supports not (field-sizing: content) {
    .expo-autosize-number .k-input-inner {
        width: 7ch;
    }
}

/* The button a toolbar folds behind on a phone or a touch tablet - Edit Sessions'. Spaced from
   what follows it as the toolbar itself is, open or shut; the caret turns over while the
   toolbar is open.

   Shown, and the toolbar folded, by a media query rather than by the page, because a tablet is
   not something width can find: an 11" iPad Pro in landscape is 1210px wide, which is a small
   laptop's width too. What sets it apart is the input - no hover, and a finger for a pointer -
   which no mouse or trackpad computer reports. Phones match that as well; the width test is
   there for a narrow desktop window, which folds as a phone does. */
.expo-toolbar-toggle {
    display: none;
    margin-bottom: 12px;
}

@media (max-width: 767px), (hover: none) and (pointer: coarse) {
    .expo-toolbar-toggle {
        display: inline-flex;
    }

    .expo-toolbar--foldable:not(.expo-toolbar--open) {
        display: none;
    }
}

.expo-toolbar-toggle__caret {
    transition: transform 150ms ease;
}

.expo-toolbar-toggle[aria-expanded="true"] .expo-toolbar-toggle__caret {
    transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
    .expo-toolbar-toggle__caret {
        transition: none;
    }
}

.expo-scroll-x {
    overflow-x: auto;
}

/* Text for screen readers only. The icon-only panel tools and grid buttons rely on this to
   carry a label; without the rule those labels render as visible text beside the icons. */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.ats-blue      { color: var(--ats-blue); }
.ats-lite-blue { color: var(--ats-lite-blue); }
.ats-orange    { color: var(--ats-orange); }

/* ==========================================================================
   Data grid
   Matches the grid in ORBITSRegReports: a bordered, rounded, Elevation-1 shell with the
   grid's own frame stripped, a solid Fleet Navy header band, zebra body rows, tabular
   numerals, themed scrollbars, and Signal Orange on the filter icon of a filtered column.
   Applied to every grid in the app rather than per page, so typed and dynamic grids match.
   ========================================================================== */

/* Every grid carries the shell look itself, so no page needs a wrapper for it. Only a grid
   that needs the repaging overlay gets an .expo-grid-shell, which then owns the frame. */
.k-grid {
    font-size: 0.875rem;
    border: 1px solid var(--kendo-color-border-alt);
    border-radius: var(--kendo-border-radius-lg);
    box-shadow: var(--kendo-elevation-1);
    overflow: hidden;

    /* Kendo gives .k-grid a border but no surface, which does not matter until the box is also
       rounded: the 1px border means the grid's inner corner is a 9px arc, while the toolbar
       inside it still rounds at the full 10px, so a sliver of the grid shows between the two.
       With nothing painted there it was a hole onto whatever sat behind — invisible against the
       page, but plain as soon as the thing behind changed colour, which is what hovering a
       dropped-down Exhibitor List row does.

       Surface rather than a tighter radius on the toolbar: the toolbar is the only child that
       rounds its own corners today, but it is not the only one that could, and a frame that
       paints its own surface cannot be seen through by the next one either. The other three
       corners never showed this — a square-cornered child is clipped by the rounded parent, so
       it over-covers rather than under-covers. */
    background-color: var(--kendo-color-surface);
}

.expo-grid-shell {
    position: relative;
    background-color: var(--kendo-color-surface);
    border: 1px solid var(--kendo-color-border-alt);
    border-radius: var(--kendo-border-radius-lg);
    box-shadow: var(--kendo-elevation-1);
    overflow: hidden;
}

/* A chart carries the same chrome as a grid, so the two read as one family on a page that
   shows both — Attendance Traffic puts them one above the other.

   Only the frame is set here. Everything inside already agrees with the grid and was measured
   rather than assumed: the labels render in Inter at 0.875rem in --kendo-color-on-surface,
   which is the grid's own body type; the gridlines are the on-surface colour at 8%, a hairline
   like the row rules; and the series take --kendo-color-series-a/b/c, the brand palette. A
   chart simply had no surface, border, radius or shadow of its own, so it sat on the page as
   loose ink beside a framed box.

   A wrapper rather than the chart element, for the same reason .expo-grid-shell is one: the
   Height parameter lands on the chart itself, and padding it shrinks the box Kendo has already
   measured its SVG against — which cuts the legend off the bottom. The shell owns the frame and
   the chart keeps the full height it was given. */
.expo-chart-shell {
    background-color: var(--kendo-color-surface);
    border: 1px solid var(--kendo-color-border-alt);
    border-radius: var(--kendo-border-radius-lg);
    box-shadow: var(--kendo-elevation-1);
    padding: 12px;
}

/* A report that draws its figures as well as listing them stacks the chart on the grid inside
   one panel — see DynamicReportPanel and Check-In Total. Both are framed surfaces, so without
   this they meet border to border and read as one box split by a hairline.

   Only when something follows: a chart that is the whole panel, which is most of them, keeps
   its own edges and does not need to push anything away. */
.expo-panel__body > .expo-chart-shell:not(:last-child) {
    margin-bottom: 1rem;
}

/* Kendo reads the label colour once and writes it into the SVG as a fill attribute, so the
   text keeps whatever the palette was when the chart drew itself and does not follow a later
   theme change. That did not show while a chart sat on the page's own background, which is
   white in both palettes; giving it a themed surface is what turns it into dark-on-dark.

   A presentation attribute loses to a stylesheet, so naming the token here keeps the text on
   whichever palette is live, including after the account menu's Dark Mode toggle. */
.expo-chart-shell svg text {
    fill: var(--kendo-color-on-surface);
}

/* The shell owns the frame, so the grid inside gives up its own. */
.expo-grid-shell .k-grid {
    border: none;
    border-radius: 0;
    box-shadow: none;
}

/* A grid or a chart is already a framed surface, so the panel around it drops its own frame
   and just supplies the title bar — the same pairing ORBITS gets from a page header plus a grid
   shell.

   The empty and loading states are in this list too, and have to be. A report that returns no
   rows renders a message instead of a grid — for the dynamic reports it has no choice, since
   the columns are read off the result set and an empty set names none — and matching only the
   grid meant the panel snapped back to its own border, grey title strip and padding the moment
   the data was empty. Same screen, two different chromes, depending on the data. */
.expo-panel:has(> .expo-panel__body > .k-grid),
.expo-panel:has(> .expo-panel__body > .expo-grid-shell),
.expo-panel:has(> .expo-panel__body > .expo-chart-shell),
.expo-panel:has(> .expo-panel__body > .expo-empty),
.expo-panel:has(> .expo-panel__body > .expo-loader),
.expo-panel:has(> .expo-panel__body > .expo-panel-card) {
    border: none;
    background: transparent;

    /* Every part of the frame, not just the visible two. Nothing here carried a shadow while
       the panels were flat, so this had nothing to clear — the exhibitor chrome adds one, and
       a shadow on a transparent box reads as a second frame floating around the grid's own. */
    box-shadow: none;
}

/* No rule below the title: the framed surface underneath already separates the title from the
   data — the grid's navy header band, or the chart's own border — so a divider on top of it
   just reads as a stray line. */
.expo-panel:has(> .expo-panel__body > .k-grid) > .expo-panel__header,
.expo-panel:has(> .expo-panel__body > .expo-grid-shell) > .expo-panel__header,
.expo-panel:has(> .expo-panel__body > .expo-chart-shell) > .expo-panel__header,
.expo-panel:has(> .expo-panel__body > .expo-empty) > .expo-panel__header,
.expo-panel:has(> .expo-panel__body > .expo-loader) > .expo-panel__header,
.expo-panel:has(> .expo-panel__body > .expo-panel-card) > .expo-panel__header {
    background: transparent;
    border-bottom: none;
    padding-left: 0;
    padding-right: 0;
    margin-bottom: 0.5rem;
}

.expo-panel:has(> .expo-panel__body > .k-grid) > .expo-panel__body,
.expo-panel:has(> .expo-panel__body > .expo-grid-shell) > .expo-panel__body,
.expo-panel:has(> .expo-panel__body > .expo-chart-shell) > .expo-panel__body,
.expo-panel:has(> .expo-panel__body > .expo-empty) > .expo-panel__body,
.expo-panel:has(> .expo-panel__body > .expo-loader) > .expo-panel__body,
.expo-panel:has(> .expo-panel__body > .expo-panel-card) > .expo-panel__body {
    padding: 0;
}

/* Panel content that is neither a grid nor a chart - a short form, a line of explanation - in
   the same framed surface, so a panel holding it reads like the grid panels beside it: the
   title on the page, the content in the card below. Compensation Report's Onsite Date is the
   first. */
.expo-panel__body > .expo-panel-card {
    padding: 16px 20px;
    background-color: var(--kendo-color-surface);
    border: 1px solid var(--kendo-color-border-alt);
    border-radius: var(--kendo-border-radius-lg);
    box-shadow: var(--kendo-elevation-1);
}

.expo-panel-card > :first-child {
    margin-top: 0;
}

.expo-panel-card > :last-child {
    margin-bottom: 0;
}

/* A rule between two parts of a page - Compensation Report's onsite-date editor and the report
   below it. The darker of the two border tokens, so it reads on the page background as well as
   the hairlines do on a card. */
.expo-section-rule {
    margin: 20px 0;
    border: 0;
    border-top: 1px solid var(--kendo-color-border);
    opacity: 1;
}

/* And the message takes the shape the grid would have had: same surface, same hairline, same
   radius and lift. An empty report then reads as the grid with nothing in it, and the frame
   does not move when the data arrives — only its contents change.

   Scoped to a direct child of a panel body, which is where a grid would otherwise be. The same
   class used for a page-level notice — "Manage Users is available to administrators only", the
   404 — is not inside one and keeps its plain treatment. */
.expo-panel__body > .expo-empty,
.expo-panel__body > .expo-loader {
    /* Most of these are a <p>, whose 1em bottom margin otherwise leaves the empty state 16px
       shorter than the grid it stands in for — the frame jumped when the data arrived. */
    margin: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.75rem;
    min-height: 180px;
    padding: 32px 24px;
    background-color: var(--kendo-color-surface);
    border: 1px solid var(--kendo-color-border-alt);
    border-radius: var(--kendo-border-radius-lg);
    box-shadow: var(--kendo-elevation-1);
}

.k-grid-toolbar {
    background-color: var(--kendo-color-surface);
    border-color: var(--kendo-color-border-alt);
}

/* The theme ships the toolbar's TextBox at 38px and its Buttons at 30px, which reads as
   controls floating at different heights in one row. Stretching the buttons takes the height
   from the tallest control instead of hardcoding one; k-button-lg only reaches 34px, so the
   component size API cannot do this. */
.k-grid-toolbar .k-button {
    align-self: stretch;
}

/* A label for a toolbar filter that needs one. The toolbar is a flex row of controls, so a bare
   <label> would sit on the row's baseline rather than centred against the box it names. */
.expo-grid-toolbar__label {
    align-self: center;
    margin-left: 4px;
    font-size: 0.875rem;
    color: var(--kendo-color-subtle, var(--text-muted));
    white-space: nowrap;
}

/* --- pager --- */

/* Kendo pins the pager to one line (.k-pager-responsive sets flex-wrap: nowrap) and hides its
   overflow, and a hidden overflow cannot be swiped — so whatever does not fit is unreachable
   rather than merely clipped. This is deliberately not inside a width query: what the pager
   needs is set by the row count and by what the sidebar leaves it, not by the viewport. A
   report long enough to run to hundreds of pages asks for more pager than a 1024px window has
   once the 250px rail is out, and the first things to fall off the end are the page-size
   dropdown and the row count. Wrapping costs a second row in exactly those cases and nothing
   at all when the row already fits. It is also the backstop for the narrow trims further down,
   if a Telerik update reshapes the markup their child selectors reach into. */
.k-grid-pager {
    flex-wrap: wrap;
    height: auto;
    row-gap: 0.25rem;
}

/* Wrapping cannot help the navigation group, which is one flex item: the page-number strip and
   the four arrows around it can be wider than the whole pager by themselves. Both levels need
   min-width: 0 before either will give — a flex item's default min-width: auto refuses to
   shrink below its content — and then the numbers scroll while the arrows, pinned, stay where
   they are. When the strip fits, which is most of the time, none of this changes how it
   looks. */
.k-grid-pager .k-pager-numbers-wrap {
    min-width: 0;
}

.k-grid-pager .k-pager-nav {
    flex-shrink: 0;
}

.k-grid-pager .k-pager-numbers {
    min-width: 0;
    overflow-x: auto;
    scrollbar-width: thin;
}

/* --- fitted columns --- */

/* A fitted grid divides one width between every column, so the columns come out far narrower
   than the width rule would have given them — sixteen of them share about a thousand pixels on
   a 1280 screen. Telerik's header puts the title and the filter button on one line and clips
   the title to whatever is left, which at that width was eighteen pixels: every heading on the
   report rendered as a bare ellipsis.

   So the title takes the full width of the cell and the filter button wraps underneath it, and
   the title wraps rather than truncating. A heading over two or three lines is legible; a
   heading cut to "…" is not, and unlike a value there is no way to recover what it said.

   Only the heading wraps. The body cells keep Telerik's single line because the grid is given a
   fixed RowHeight — the pager's All mode is virtual scrolling, which works out which rows to
   draw by dividing the scroll offset by that number — so a cell that grew to two lines would be
   cut off by the row rather than making it taller. */
.expo-grid-shell--fit .k-grid-header .k-cell-inner,
.k-grid.expo-grid--fit .k-grid-header .k-cell-inner {
    flex-wrap: wrap;
}

.expo-grid-shell--fit .k-grid-header .k-link,
.k-grid.expo-grid--fit .k-grid-header .k-link {
    flex: 1 1 100%;
    overflow: visible;
}

.expo-grid-shell--fit .k-grid-header .k-column-title,
.k-grid.expo-grid--fit .k-grid-header .k-column-title {
    white-space: normal;
    overflow: visible;
    overflow-wrap: anywhere;
}

/* And the cells give back their side padding, which is the difference between a count fitting
   and not. The hourly columns come out at 45px on a 1280 screen; Telerik's own padding spends
   24 of that on empty space either side of a three-digit number that needs 24 to draw, so "296"
   lost its last digit while a third of the column stood empty. Only on a fitted grid — the
   ordinary report grids have room and keep the roomier cell. */
.expo-grid-shell--fit .k-grid-header .k-table-th,
.expo-grid-shell--fit .k-table-td,
.k-grid.expo-grid--fit .k-grid-header .k-table-th,
.k-grid.expo-grid--fit .k-table-td {
    padding-left: 6px;
    padding-right: 6px;
}

/* A grid fitted outside ReportGrid - Attendance Traffic's - carries the same rules on the grid
   itself, as expo-grid--fit, there being no shell around it to hang them on. Sessions Attended
   does not: it keeps each heading's filter button on the title's line - see its GridClass. */

/* A fitted grid whose text columns wrap rather than cut - Sessions Attended. They share what
   the measured columns leave, which on a tablet can be narrower than a long word in a class
   title, so a word that does not fit breaks rather than running under the next column. Not in
   All, whose rows are one 40px line by construction and end in an ellipsis instead - see
   .expo-grid--all.

   Body rows only. The footer's "Total Participants" label runs across the empty cells beside
   it on one line - see .expo-grid-footer__label - and this rule, being the more specific,
   was folding it into its own 106px column instead. */
.k-grid.expo-grid--wrap:not(.expo-grid--all) .k-grid-table .k-table-td {
    white-space: normal;
    overflow-wrap: anywhere;
}

/* A cell's text over two set lines, each kept whole: a date over its time on Sessions
   Attended, "Totals for" over "N Day(s)" under Attendance Traffic's dates. The pair is an
   inline-block so that it is as wide as its longer line rather than its cell, which is what
   ColumnTextFit measures: lines stretched to the cell measured as wide as the column already
   was, and the column grew on every pass. */
.expo-two-line {
    display: inline-block;
}

.expo-two-line > span {
    display: block;
    white-space: nowrap;
}

/* The traffic grid's dates stay on one line at every size. The column is measured to hold the
   longest - see ColumnTextFit - so this only matters before the first measurement lands, and
   keeps that one frame from breaking a date in two. The grid has no locked or hidden leading
   column, so the date is always the first cell. */
.expo-traffic-grid .k-table-td:first-child {
    white-space: nowrap;
}

/* --- totals footer --- */

/* Only a report that asks for one has a footer at all — ReportGrid leaves the row off unless a
   caller names a column to total, which today is Check-In by Date Time.

   It has to read as a summary rather than one more day: the body rows are striped white and
   subtle, so a plain cell here just looks like a fourth row with a word in it. The rule above
   it and the heavier type are the whole distinction. */
.k-grid-footer .k-table-td {
    font-weight: 700;
    color: var(--kendo-color-on-surface);
    border-top: 2px solid var(--kendo-color-border);
}

/* The totalled cell takes its column's alignment. Telerik applies TextAlign to the header and
   body cells and not to the footer, so without this the figure sits left under a right-aligned
   column of numbers and does not line up with the values it adds. Matching on the wrapper
   rather than a column index because the summed column is wherever the report put it. */
.k-grid-footer .k-table-td:has(> .expo-grid-footer__sum) {
    text-align: right;
}

/* A free-text cell - see ReportGrid's LongTextColumns - shows two lines at most and ends in an
   ellipsis, so one long note cannot make its row many times the height of the rest. The full
   value is on the element's title. overflow-wrap: anywhere so that a URL or an unbroken run of
   characters is cut at the column edge rather than pushing the clamp off to one side. */
.expo-cell-clamp {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    overflow-wrap: anywhere;
}

/* In All every row has to be exactly 40px, and a reading cell there is already one line with
   an ellipsis (see .expo-grid--all), so a free-text cell does the same instead of two. */
.k-grid.expo-grid--all .expo-cell-clamp {
    display: block;
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* The label runs across its neighbours rather than wrapping inside its own column. Every cell
   between the label and the total is empty — that is what "only a total for the Total column"
   means — so there is nothing for the text to collide with, and a first column narrow enough to
   split "Totals for all days" over two lines would otherwise make the whole footer row two
   lines tall to hold it. */
.k-grid-footer .k-table-td:has(> .expo-grid-footer__label) {
    overflow: visible;
    white-space: nowrap;
}

/* --- header band --- */

.k-grid-header {
    background-color: var(--kendo-color-primary);
}

.k-grid-header .k-table-th {
    font-weight: 700;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--kendo-color-on-primary);
    background-color: var(--kendo-color-primary);
    /* Hairline column separators, since a border would darken the band. */
    box-shadow: inset -2px 0 0 rgba(255, 255, 255, 0.25);
}

.k-grid-header .k-table-th:last-child {
    box-shadow: none;
}

.k-grid-header .k-table-th .k-svg-icon,
.k-grid-header .k-table-th .k-icon,
.k-grid-header .k-table-th .k-sort-icon,
.k-grid-header .k-table-th .k-sort-order {
    color: var(--kendo-color-on-primary);
}

.k-grid-header .k-table-th:hover {
    background-color: var(--kendo-color-primary-hover);
}

.k-grid-header .k-table-th.k-sorted {
    background-color: var(--kendo-color-primary-active);
}

/* Telerik marks a filtered column with k-active on its filter button, but that style is a
   Fleet Navy fill — invisible against this navy band, so an applied filter read as no
   indicator at all. Signal Orange on the icon is what makes it legible. The token carries its
   own dark-theme value, so this needs no second rule. */
.k-grid-header .k-table-th .k-grid-filter-menu.k-active,
.k-grid-header .k-table-th .k-grid-filter-menu.k-active:hover {
    background-color: transparent;
}

.k-grid-header .k-table-th .k-grid-filter-menu.k-active .k-svg-icon,
.k-grid-header .k-table-th .k-grid-filter-menu.k-active .k-icon {
    color: var(--kendo-color-tertiary);
}

/* --- body --- */

/* A row keeps its height when it goes into edit.

   Telerik tightens the padding of every cell in an edit row — `.k-grid .k-table
   .k-grid-edit-row .k-table-td` sets padding-block to 4px against the 11px an ordinary cell
   gets. That is right for the cell holding the editor, because a 38px input in 4px of padding
   comes to the same 46px as a line of text in 11px, and a single-line row keeps its height.

   It is wrong for the cells either side of it, which still hold text. A row whose exhibitor
   name or email has wrapped to two lines is 48px of text in 22px of padding, and losing 14px of
   that padding is a visible lurch the moment Edit Booth is pressed. Those cells keep the
   ordinary padding; the editor and the buttons keep the tight one.

   11px is what this theme gives an ordinary cell. It is a number rather than a token because
   the theme resolves it from Sass at build time and exposes no custom property to read it from. */
.k-grid .k-table .k-grid-edit-row .k-table-td:not(.k-edit-cell):not(.k-grid-edit-cell):not(.k-command-cell) {
    padding-block: 11px;
}

/* Not in All, where every row is 40px and a reading cell already has 8px of padding: the 11px
   above put a 45px row into a virtual list whose arithmetic assumes 40, and the rows below it
   slid out of step with the scrollbar the moment Edit was pressed. Written with the same
   :not() chain so that it outranks the rule above, which three :not()s lift to (0,7,0). */
.k-grid.expo-grid--all .k-table .k-grid-edit-row .k-table-td:not(.k-edit-cell):not(.k-grid-edit-cell):not(.k-command-cell) {
    padding-block: 8px;
}

/* Columns of numbers align on their digits. */
.k-grid .k-table-td {
    font-variant-numeric: tabular-nums;
}

/* No zebra striping: every cell is surface-alt, so a column reads straight down and reordering
   the rows by a sort cannot make a banding pattern appear out of nowhere.

   The stripe is not drawn where it looks like it is. Telerik paints the body TABLE with
   surface-alt already and leaves the odd rows transparent to let it through, painting only the
   even ones in surface — so recolouring .k-table-alt-row swaps which rows are tinted instead of
   stopping the tinting. The fix is to stop the even rows painting over it: the table keeps the
   tint, and the alt row goes back to transparent so it shows that tint like every other row. */
.k-grid .k-grid-table {
    background-color: var(--kendo-color-surface-alt);
}

.k-grid .k-table-alt-row {
    background-color: transparent;
}

/* Save and Cancel on a row being edited.

   They stack: Exhibitor List gives the command column 200px, and Save (81px) plus Cancel (96px)
   plus the gap between them and the cell's own 20px of padding comes to 201px, so the pair
   misses fitting on one line by a pixel. Stacked they were ragged, because the two labels are
   different lengths and each button is only as wide as its own.

   96px is what Cancel measures in Inter at 16px — icon, label and the button's 10px of padding
   either side — so Save grows to meet it and the two square up. Every fallback face in the
   stack sets narrower than Inter, so this is the wide case; it is a floor, not a width, and a
   longer label would simply push past it.

   The 3px of vertical margin that used to sit here went when the stacking did: with the buttons
   on one line it only made the cell 6px taller than the text cells beside it, which is a row
   that grows the moment it goes into edit.

   Scoped to the edit row so the Edit / Booth / Delete buttons on an ordinary row keep their
   natural widths, and applied to every inline-edit grid — ManageUsers, Qualifiers and the
   session editor all show the same pair, and their columns are wide enough that this only
   evens the two up rather than wrapping them. */
.k-grid .k-grid-edit-row .k-command-cell .k-button:not(.k-icon-button) {
    min-width: 96px;
}

/* The session editor's frozen command column wraps its buttons in a span, and in a column too
   narrow for Save and Cancel side by side they stack, flush against each other. A wrapping flex row
   with a 4px gap spaces them either way: 4px apart one above the other where they stack, and
   4px apart side by side where there is room, which is what the whitespace between the buttons
   gave them before. Edit rows only; an ordinary row keeps its plain inline buttons. */
.k-grid .k-grid-edit-row .k-command-cell > span {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 4px;
}

/* Exhibitor List's row actions - Edit Booth and Leads in one column.

   A download link has nothing to do with the row you are part-way through editing, and while
   it is on screen the column has to be wide enough to hold it beside Save and Cancel — 110px
   that every resting row then carries for a state it is not in. Telerik hides its own command
   buttons by edit state; an anchor is ours, so it is hidden here. */
.expo-row-actions .k-grid-edit-row .k-command-cell a.k-button {
    display: none;
}

/* The same for a session editor's extra row buttons (SessionTableEditor.RowActions): while a
   row is in edit, Save and Cancel take its command cell, and a download beside them would push
   them out of the frozen column. */
.k-grid .k-grid-edit-row .k-command-cell .expo-row-action {
    display: none;
}

/* A grid whose command column hugs its buttons - Exhibitor List and the session editors: Edit
   Attendance, Edit Sessions, Cert Sessions. The pages size the column from the buttons it shows;
   see CommandWidths.

   The column hugs the resting buttons, and Save and Cancel get whatever that leaves. They keep
   their natural widths here rather than the 96px the rule above evens them to, which is what
   lets them sit side by side in Exhibitor List's 203px. Where even that does not fit - Cert
   Sessions, and the scans nested in Edit Attendance, whose Edit and Delete come to 169px
   against their 184 - the edit row's wrapping flex row above stacks them, and flex-grow has
   each fill the column, as the stacked buttons on a phone do. On a wide column the pair still
   sits on one line and the span is only as wide as they are, so there is nothing to grow
   into. */
.k-grid.expo-hug-actions .k-grid-edit-row .k-command-cell .k-button:not(.k-icon-button) {
    min-width: 0;
    flex-grow: 1;
}

/* A resting row's buttons stay on one line. The column is sized to fit them - see
   CommandColumnFit, which measures the line this keeps whole - and a column a pixel or two
   short then clips the last button's edge for a moment, rather than dropping it onto a second
   line and doubling the height of every row. */
.k-grid.expo-hug-actions .k-master-row:not(.k-grid-edit-row) .k-command-cell > span {
    white-space: nowrap;
}

/* On a phone the same grids stack the buttons one above the other, each the column's full
   width, so the column is one button wide rather than five.

   Telerik wraps a command cell's buttons in a span, frozen column or not, and that span becomes
   the stacking column; stretch does the widths. The whitespace between the tags, which was the
   gap side by side, means nothing in a flex column, hence the gap. Nothing here sets a button's
   display, so the rules hiding a row's extra buttons while it is in edit still win.

   .k-grid and .k-table-row are there for weight: they outrank the edit row's wrapping flex row
   above, so Save and Cancel fill the column as the resting buttons do instead of keeping to
   their 96px. */
@media (max-width: 767px) {
    .k-grid.expo-hug-actions .k-table-row .k-command-cell > span {
        display: flex;
        flex-direction: column;
        align-items: stretch;
        gap: 4px;
    }

    .k-grid.expo-hug-actions .k-command-cell > span > .k-button {
        margin: 0;
    }
}


/* The expand / collapse cell on a row that has a detail template.

   Telerik renders the plus and minus as a bare <span> holding an SVG — not a button and not a
   link — so it inherited the text cursor from the cell and gave no sign it could be clicked.

   The cursor goes on the whole cell rather than on the 16px icon, because the whole cell is
   what toggles: clicking 4px in from the cell's left edge, nowhere near the glyph, opens the
   row. A pointer on the icon alone would have described a smaller target than the one that is
   actually there. Master rows only — the header's copy of this cell is an empty spacer, as is
   the one on the detail row itself, and neither does anything when clicked. */
.k-grid .k-master-row > .k-hierarchy-cell {
    cursor: pointer;
}

/* A detail cell spans every column of the grid above it, so a grid dropped inside one inherits
   all of that width — on Exhibitor List, 1358px of table for three columns that between them
   ask for 470px, each then stretched to nearly three times the width of anything in it.

   The width itself is set on the component, because CSS cannot work it out here: the obvious
   width:max-content comes back as the full 1358px, since the thing being measured is a table at
   width:100% inside an overflow:auto scroller, which has no intrinsic width of its own to
   report. See GridWidths.ForHeaders, which totals the column set instead.

   What is left here is the guard for the other direction: on a window narrower than the grid's
   own width, it goes back to filling what it has and scrolls inside itself rather than pushing
   the page sideways. */
.k-detail-cell > .k-grid {
    max-width: 100%;
}

/* --- frozen (sticky) columns --- */

/* Telerik implements frozen columns with position:sticky rather than a separate pane, and
   paints those cells opaque so scrolling rows don't show through them. Two consequences to
   undo: the sticky header cell is painted surface-white, leaving a gap in the navy band; and
   every sticky body cell is painted surface-alt, which flattens the zebra down the frozen
   column. Telerik's own selectors are specific, so these have to out-specify them. */
.k-grid-header .k-table-th.k-grid-header-sticky {
    background-color: var(--kendo-color-primary);
    color: var(--kendo-color-on-primary);
}

.k-grid-header .k-table-th.k-grid-header-sticky .k-svg-icon,
.k-grid-header .k-table-th.k-grid-header-sticky .k-icon,
.k-grid-header .k-table-th.k-grid-header-sticky .k-sort-icon,
.k-grid-header .k-table-th.k-grid-header-sticky .k-sort-order {
    color: var(--kendo-color-on-primary);
}

.k-grid-header .k-table-th.k-grid-header-sticky:hover {
    background-color: var(--kendo-color-primary-hover);
}

.k-grid-header .k-table-th.k-grid-header-sticky.k-sorted {
    background-color: var(--kendo-color-primary-active);
}

/* A filtered frozen column still shows Signal Orange, same as any other. */
.k-grid-header .k-table-th.k-grid-header-sticky .k-grid-filter-menu.k-active .k-svg-icon,
.k-grid-header .k-table-th.k-grid-header-sticky .k-grid-filter-menu.k-active .k-icon {
    color: var(--kendo-color-tertiary);
}

/* A frozen cell has to paint an opaque background or the scrolling cells show through it as
   they pass underneath — it cannot inherit the table's fill the way a transparent cell does. It
   takes the same colour as every other cell, so the freeze line is invisible until you scroll,
   which is the point.

   Telerik's own rule for this carries six classes, so this matches that shape to win on
   specificity: a shorter selector silently loses. Six and no more, though. Telerik's HOVER rule
   for a frozen cell is seven, and this must stay under it so the frozen cells take the hover
   tint along with the rest of their row. An alt-row variant used to be listed here "to match
   the shape", and its seventh class tied that hover rule - which this file, loading last, then
   won - so on every other row of Edit Sessions and Edit Attendance the command cell sat pale
   while the row around it darkened. */
.k-grid .k-master-row.k-table-row > .k-table-td.k-grid-content-sticky:not(.k-group-cell) {
    background-color: var(--kendo-color-surface-alt);
}

/* A hairline marks where the frozen columns end and the scrolling ones begin. */
.k-grid .k-table-th.k-grid-header-sticky,
.k-grid .k-table-td.k-grid-content-sticky {
    border-inline-end: 1px solid var(--kendo-color-border);
}

.k-grid-content {
    scrollbar-width: thin;
    scrollbar-color: var(--kendo-color-base-subtle-active) transparent;
}

.k-grid-content::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}

.k-grid-content::-webkit-scrollbar-thumb {
    background-color: var(--kendo-color-base-subtle-active);
    border-radius: var(--kendo-border-radius-full, 9999px);
}

/* ==========================================================================
   "All" — the pager's second choice, which is virtual scrolling
   ========================================================================== */

/* Virtual scrolling works out which rows to show from the scroll offset arithmetically: it
   sizes one tall spacer to RowHeight x row count and divides scroll position back by RowHeight.
   So every row has to be exactly RowHeight tall or the two disagree, and the further you scroll
   the worse it gets — at tens of thousands of rows the end of the report ends up past the
   bottom of its own scrollbar and the last screenful comes back blank.

   Left alone a row is 46.889px: 11px padding twice, a 24px line, and a border landing on a
   fraction of a pixel, which is what makes rows measure 46 and 47 alternately. The geometry is
   pinned here to whole pixels adding up to GridPaging.RowHeightPx — that constant and these
   rules are one setting in two files.

   Only in this mode. Paged rows keep wrapping, which is what keeps multi-line values readable
   there, and columns stay resizable so a value cut off here can be opened up. */
.k-grid.expo-grid--all .k-grid-table .k-table-row {
    height: 40px;
}

/* A cell holding a control gives up most of its padding, because the 40px row All depends on
   has to hold a 30px button: the action buttons on Exhibitor List and Attendance Traffic, the
   Edit/Save/Delete commands and the inline editors on the session tables. Measured, 8px over
   8px put those rows at 55px and the scrollbar out of step with the rows - which is why those
   grids went without All until now. 5px either side centres the control in the 40; cells with
   only text keep the padding below. Higher specificity than the rule under it, so order does
   not decide. */
.k-grid.expo-grid--all .k-table-td:has(.k-button, .k-input, .k-picker) {
    padding-top: 5px;
    /* 4, not 5: the cell is a 40px border-box with a 1px bottom border, so its padding and the
       30px control have 39 to share between them. 5 over 5 measured 41. */
    padding-bottom: 4px;
    line-height: 30px;
}

/* And the controls themselves come down to 30px, because trimming the padding alone was not
   enough: Meridian's medium button and editor are 38px tall - the same height that made the
   header 62px instead of 56 - so a cell holding one still measured 49. 30 plus the 5px either
   side is the 40 the row has to be. The button's label keeps its size; only the box shrinks. */
.k-grid.expo-grid--all .k-table-td .k-button,
.k-grid.expo-grid--all .k-table-td .k-input,
.k-grid.expo-grid--all .k-table-td .k-picker {
    height: 30px;
    min-height: 0;
    padding-top: 0;
    padding-bottom: 0;

    /* These are inline-flex boxes sitting on the cell's text baseline, and a 30px box on a
       baseline leaves the line box taller than the box by the descender space under it -
       measured, the row came out at 43 with the controls already at 30. Aligned to the top of
       the line instead, the line is exactly as tall as the control. */
    vertical-align: top;
}

.k-grid.expo-grid--all .k-table-td .k-input-inner {
    padding-top: 0;
    padding-bottom: 0;
}

.k-grid.expo-grid--all .k-table-td {
    box-sizing: border-box;
    height: 40px;
    padding-top: 8px;
    padding-bottom: 8px;
    line-height: 22px;
    border-bottom-width: 1px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* An empty result leaves the scroller mid-stride: Telerik resets the spacer that sizes the
   scrollbar to zero, but not the transform holding the rows at the offset they were last read
   for. Left alone the content keeps its whole stride inside a container it has just collapsed,
   so the grid scrolls through a void. The no-records row is the only marker of that state that
   reaches CSS, which is what both of these hang off — a display:none element still answers
   :has(). */
.k-grid.expo-grid--all .k-virtual-scroller-content:has(.k-grid-norecords) {
    transform: none !important;
}

/* And the buffer keeps its rows when there is nothing to put in them, each still RowHeight
   tall — sixty of those is another 2,400px of empty scroll below the message.

   :not() is load-bearing. Telerik's "No records available." row carries .k-table-row itself, so
   without it this rule hides the very message it is clearing space for, and an empty result
   reads as a blank grid with no explanation. */
.k-grid.expo-grid--all .k-table-tbody:has(.k-grid-norecords) .k-table-row:not(.k-grid-norecords) {
    display: none;
}

/* The single-condition filter menu: one operator dropdown over one typed editor. */
.expo-filter-menu {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    min-width: 200px;
}

/* Telerik's stock filter menu presents two conditions joined by And/Or. Two inputs read as a
   query builder, and this is a browsing-and-export layer rather than a query tool — the second
   condition is cost with no audience. There is no setting for it (FilterMenuType and
   FilterOperators exist; a condition count does not): the dynamic ReportGrid trims the seeded
   descriptor list in a FilterMenuTemplate, and this is the same rule for the typed grids, which
   use Telerik's own menu rather than carrying a template on every one of their columns.

   Hiding rather than removing is what makes it safe. Telerik drops a value-less descriptor when
   it commits the filter, so the blank second condition simply never applies — whereas removing
   it from the collection outright breaks the commit path and the Filter button silently does
   nothing.

   The container's children are flat: operator, editor, And/Or, operator, editor, actions. This
   hides everything from the third child up to but not including the last, so it does not depend
   on how many elements an editor of a given type renders, and it collapses to hiding nothing if
   Telerik ever ships a single-condition menu of its own. */
.k-filter-menu-container > *:nth-child(n+3):nth-last-child(n+2) {
    display: none;
}

/* On a phone Telerik keeps every toolbar control in one nowrap strip with overflow-x auto, so
   they squeeze rather than wrap and the search box ends up unusably narrow. Stacking them
   restores full width; the button stretch above crosses over to horizontal when the direction
   flips. Keep this threshold in step with the layout breakpoints below. */
@media (max-width: 767px) {
    :root {
        --viewport: narrow;
    }

    /* The pager's overflow is hidden and a hidden overflow cannot be swiped, so anything that
       does not fit is not merely ugly — it is unreachable. The grids already swap the
       page-number strip for a page input at this width; these are the remaining parts, dropped
       in the order they stop earning their width. First and last are redundant beside an input
       that accepts 1 or the last page number; the row count restates what the input's own
       "of N" and the page size already say; and a size dropdown inside a pager does not need
       the words "items per page" beside it. */
    .k-grid-pager .k-pager-info,
    .k-grid-pager .k-pager-sizes > span:last-child,
    .k-grid-pager .k-pager-first,
    .k-grid-pager .k-pager-last {
        display: none;
    }

    /* "Page" leaves the layout but stays announced: without it the number box has no label at
       all, and the remaining "of N" sits after the value rather than before it. Hiding it is
       what buys the fit at 320px. */
    .k-grid-pager .k-pager-input > span:first-child {
        position: absolute;
        width: 1px;
        height: 1px;
        margin: -1px;
        padding: 0;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }

    .k-grid-toolbar .k-toolbar-items {
        flex-direction: column;
        align-items: stretch;
    }

    /* Telerik writes a control's Width as an inline style on its wrapper, so nothing short of
       !important reaches it. Stacked, a 240px search box floating in the middle of a 343px strip
       reads as broken rather than as a deliberate size. */
    .k-grid-toolbar .k-toolbar-items > .k-input,
    .k-grid-toolbar .k-toolbar-items > .expo-grid-toolbar__search > .k-input,
    .k-grid-toolbar .k-toolbar-items > .k-picker {
        width: 100% !important;
    }

    /* Centring is what keeps a label level with the box beside it in a row; stacked, the box is
       below it and the label belongs at the left edge. */
    .k-grid-toolbar .k-toolbar-items > .expo-grid-toolbar__label {
        align-self: flex-start;
        margin-left: 0;
    }
}

/* ==========================================================================
   Telerik overrides — remaining components
   ========================================================================== */

.k-list-item.k-selected,
.k-selected {
    background-color: var(--kendo-color-primary);
    color: var(--kendo-color-on-primary);
}

/* The icon buttons inside an input - a numeric box's spinners, a date or time picker's
   calendar and clock, a combo box's or multiselect's arrow - in the box's own text colour, as a
   drop-down list's arrow already is.

   The theme gives these buttons a background of their own (surface-alt) but no colour, so they
   fell back to .k-button's on-base. This file sets on-base to white, for text on the slate base
   buttons, which left a white icon on a pale #EEF2F8 square - all but invisible. A drop-down
   list's arrow is not affected: the theme sets its colour to inherit, which is all this does
   for the rest. */
.k-input .k-input-button,
.k-input .k-spinner-increase,
.k-input .k-spinner-decrease {
    color: inherit;
}

/* ==========================================================================
   Responsive
   ========================================================================== */

@media (max-width: 991px) {
    .expo-sidebar {
        transform: translateX(-100%);
        transition: transform 0.2s ease;
    }

    /* Over the page now rather than beside it, so it takes the one elevation the app keeps
       for things that float: elevation 3. */
    .expo-sidebar--open {
        transform: translateX(0);
        box-shadow: var(--kendo-elevation-3);
    }

    .expo-page {
        margin-left: 0;
        border-radius: 0;
    }
}

@media (min-width: 992px) {
    /* The sidebar toggle is only offered on small screens: here the rail is always on screen
       beside the page and there is nothing for the button to reveal. */
    .expo-navbar__toggle {
        display: none;
    }

    /* The rail takes the whole left edge, from the top of the window, and the header starts
       where the rail ends - the reference's push drawer beside its header, which is also why
       the header keeps the navy at this width rather than turning into a light bar. */
    .expo-sidebar {
        top: 0;
    }

    .expo-navbar {
        margin-left: var(--sidebar-width);
    }

    /* A taller bar on desktop, with the wordmark at its full 200px. The token rather than the
       bar's own height, because everything that sizes itself against the bar - the page's
       minimum height, the full-height grid pages - reads the token, and a bar that grew on its
       own would leave them all 44px short. The 452x144 asset at 200px wide stands 64px tall,
       which sits inside the 100 with the bar's 12px of padding either side. */
    :root {
        --navbar-height: 100px;
    }

    .expo-navbar__brand img {
        width: 200px;
        height: auto;
    }

    /* The page's rounded top-left corner used to tuck under the header and vanish. The two
       surfaces meet square, on their own fills. */
    .expo-page {
        border-radius: 0;
    }
}

/* ---------- the rail, collapsed ----------

   Tablets in landscape and desktops keep the rail beside the page, and the caret on its edge
   tucks it away the way the phone drawer closes: off the left edge, with the bar and the page
   moving over to take the room. The bar's menu button comes back to bring it out again.

   The state is a class on <html> rather than anything Blazor holds, so the inline script in
   App.razor can set it from localStorage before the first paint - a rail restored by the
   circuit a moment later would slide shut in front of the user on every reload. Everything
   below is scoped to this width, so the class does nothing to the drawer on smaller screens. */

/* Off at every width; the query below is where it comes on. */
.expo-sidebar__collapse,
.expo-navbar__toggle--expand {
    display: none;
}

@media (min-width: 992px) {
    .expo-sidebar,
    .expo-sidebar__collapse {
        transition: transform 0.2s ease, opacity 0.2s ease, visibility 0.2s;
    }

    .expo-navbar,
    .expo-page {
        transition: margin-left 0.2s ease;
    }

    /* A small disc straddling the rail's hairline, level with the middle of the bar. Fixed
       rather than inside the rail, which clips its own overflow. Surface with a Border ring and
       elevation 1 - a control resting on the seam, not a layer over the page. */
    .expo-sidebar__collapse {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        position: fixed;
        top: calc(var(--navbar-height) / 2 - 12px);
        left: calc(var(--sidebar-width) - 12px);

        /* Over the bar, which is sticky at 4 and would otherwise cover the half on its side. */
        z-index: 5;
        width: 24px;
        height: 24px;
        padding: 0;
        border: 1px solid var(--kendo-color-border);
        border-radius: 50%;
        background-color: var(--kendo-color-surface);
        color: var(--kendo-color-subtle);
        box-shadow: var(--kendo-elevation-1);
        font-size: 12px;
        line-height: 1;
        cursor: pointer;
    }

    /* Hovered, pressed or focused, the lighter ATS orange - :focus rather than :focus-visible,
       so a click shows it as well as a tab. */
    .expo-sidebar__collapse:hover,
    .expo-sidebar__collapse:focus,
    .expo-sidebar__collapse:active {
        outline: none;
        background-color: var(--ats-orange);
        border-color: var(--ats-orange);
        color: #fff;
    }

    /* A 24px disc is a fine thing to see and a small thing to hit with a finger; the target is
       grown invisibly to 40px around it. */
    .expo-sidebar__collapse::before {
        content: "";
        position: absolute;
        inset: -8px;
        border-radius: 50%;
    }

    /* The chevron sits a hair left of centre in the glyph box; nudged back. */
    .expo-sidebar__collapse i {
        margin-left: -1px;
    }

    html.expo-sidebar-collapsed .expo-sidebar,
    html.expo-sidebar-collapsed .expo-sidebar__collapse {
        transform: translateX(calc(-1 * var(--sidebar-width)));
    }

    /* Hidden as well as moved, so it is out of the tab order and out of reach of a stray tap
       on the strip of it that would otherwise still show at the window's edge. */
    html.expo-sidebar-collapsed .expo-sidebar {
        visibility: hidden;
    }

    html.expo-sidebar-collapsed .expo-sidebar__collapse {
        opacity: 0;
        visibility: hidden;
    }

    html.expo-sidebar-collapsed .expo-navbar,
    html.expo-sidebar-collapsed .expo-page {
        margin-left: 0;
    }

    html.expo-sidebar-collapsed .expo-navbar__toggle--expand {
        display: inline-flex;
    }
}

/* Touch tablets get the phone's bar: 56px tall with the 26px logo, rather than the desktop's
   100px with the wordmark at 200px wide. A landscape iPad is as wide as a laptop, so width
   cannot tell them apart; the input can - see the Actions toggle, which keys off the same
   query. The bar stays pinned here, unlike on a phone, since the rail beside it does not cover
   it. Restating the token is what carries the change to everything sized against the bar. */
@media (min-width: 768px) and (hover: none) and (pointer: coarse) {
    :root {
        --navbar-height: 56px;
    }

    .expo-navbar__brand img {
        width: auto;
        height: 26px;
    }

    /* A full-height grid page takes its height from the screen itself - html and body at 100%,
       the bar and the page sharing it as a column - rather than from 100dvh less the bar. On an
       iPad both Safari and Chrome lay the page out partly under their own toolbars and count
       that strip in vh and dvh alike, so the calc ran the pager off the bottom, by more in
       Chrome, whose toolbar is taller. A percentage of the root resolves against the viewport
       the browser actually shows, the one the rail's pinned footer lands on the bottom of.

       Kept in flow, unlike pinning the page with position: fixed, which was tried first: that
       left the bar as the only thing in the document, so a pull-to-refresh slid the bar down
       over a page that stayed put, and body's 100vh floor still left a strip of empty document
       to scroll. Here the document is exactly the screen, and a pull moves the bar and the page
       together the way it does on any other page.

       The phone layout below is the same column, measured with dvh instead; that is right on a
       phone and wrong here. Every :has() is one flat chain - nesting them is invalid. */
    html:has(.expo-page > .expo-page__body > .expo-panel--fill > .expo-panel__body) {
        height: 100%;
    }

    body.k-body:has(.expo-page > .expo-page__body > .expo-panel--fill > .expo-panel__body) {
        height: 100%;
        min-height: 0;
        display: flex;
        flex-direction: column;
    }

    body.k-body:has(.expo-page > .expo-page__body > .expo-panel--fill > .expo-panel__body) > .expo-navbar {
        flex: none;
    }

    /* The page's own height and its 100vh-less-the-bar floor both carry the same overcount,
       so both go; flex hands it whatever the bar leaves. */
    .expo-page:has(> .expo-page__body > .expo-panel--fill > .expo-panel__body) {
        flex: 1 1 auto;
        height: auto;
        min-height: 0;
    }
}

@media (min-width: 992px) and (prefers-reduced-motion: reduce) {
    .expo-sidebar,
    .expo-sidebar__collapse,
    .expo-navbar,
    .expo-page {
        transition: none;
    }
}

@media (max-width: 767px) {
    /* The bar scrolls away with the page here rather than staying pinned: at this width the
       rail covers it when open, and the toggle comes back with a scroll to the top. One row
       now - the show name that used to wrap it onto three has moved into the rail's card. */
    .expo-navbar {
        position: static;
        gap: 0.625rem;
    }

    /* The reference's phone header: the logo a step smaller and capped so it never takes the
       whole bar, letterboxed by object-fit rather than squashed. */
    .expo-navbar__brand img {
        height: 26px;
        max-width: 45vw;
    }

    /* The drawer is a layer over the page here, and at 320px a 280px rail leaves a strip of
       the page too thin to read as something behind it. Capped, the page stays visible enough
       to be worth overlaying, and the menu still has its full width to itself. */
    .expo-sidebar {
        max-width: 85vw;
    }

    /* The account button has moved into the sidebar at this width and MainLayout stops
       rendering it here, so this rule covers exactly one frame: the first paint, before
       viewport.js has reported the width back and IsNarrow is still false. Without it the
       button appears in the bar and then jumps out of it. */
    .expo-navbar .expo-user-menu {
        display: none;
    }

    /* The page's height comes from flow here, not from 100vh minus a token. --navbar-height is
       a floor rather than a measurement at this width: the bar is static and wraps onto two
       rows, so its real height depends on how long the selected show's name is — 105px for a
       typical one and 143px for a long one, against the 100px the token claims. Subtracting the
       token left every page a few pixels too tall, so a screen with one line on it still
       scrolled. A flex column cannot be wrong about it: the bar takes what it takes and the
       page takes the rest.

       Only the bar and the page become flex items. The rail, the scrim and Telerik's popup
       containers are all positioned, which takes them out of flow.

       The 100vh this column measures against is already set on body far above; nothing needs
       restating here, and a bare `body` could not restate it anyway — see the @supports note. */
    body {
        display: flex;
        flex-direction: column;
    }

    /* dvh where it exists, for the same reason the full-height grids use it: a mobile browser
       counts its collapsing address bar inside 100vh, which leaves the foot of the page just
       below the fold at rest.

       Written at `body.k-body`'s specificity to match the rule it is overriding. The height
       above is set by `body, body.k-body` (0,1,1) and a bare `body` is 0,0,1, so it loses
       wherever it sits in the file — this override would have been decorative. */
    @supports (min-height: 100dvh) {
        body,
        body.k-body {
            min-height: 100dvh;
        }
    }

    .expo-page {
        flex: 1 1 auto;

        /* The calc() floor this replaces. Left in place it still wins on a short page, which is
           the stray scroll back again. */
        min-height: 0;
    }

    /* Full height of the screen rather than starting under the navbar. The bar is static at
       this width — it scrolls away with the page instead of staying pinned — so a rail held at
       top: var(--navbar-height) showed a strip of the page above itself the moment anything was
       scrolled, and the strip was as tall as however many rows the bar had wrapped onto. */
    .expo-sidebar {
        top: 0;

        /* Over the scrim, which is itself over the page — see .expo-sidebar__scrim. */
        z-index: 4;
    }

    /* Only at this width is the toggle behind the drawer, so only here is the scrim the way
       out. Its appearance is defined with the sidebar above; this is the switch. */
    .expo-sidebar__scrim {
        display: block;
    }

    /* The page held still behind an open drawer. Set from script — see expoSidebar.lockScroll —
       because whether the drawer is open is Blazor's state, not something a selector can see.

       The root element is the one that counts. body carries an overflow-y of its own from the
       rule far above, which leaves <html> as the document's scroller: hidden on the body was
       measured here and did not stop a wheel gesture at all, while hidden on the root held the
       page at nought. The body is taken too, for the browsers that scroll it instead — and
       qualified past `body, body.k-body` (0,1,1), which it would otherwise lose to.

       Scoped to this width as well as gated in C#, so a class left behind by a resize does
       nothing on a wider screen. */
    html.expo-scroll-locked,
    html.expo-scroll-locked body.k-body {
        overflow: hidden;
    }

    /* The account menu's popup, matched to the button it drops from. Telerik renders it at the
       end of the body, so it inherits nothing from the rail and has to be given the same
       arithmetic the button gets from its slot: the rail's width less the 20px inset on each
       side of .expo-sidebar__account. Scoped to this width because that is where the menu is in
       the sidebar at all — in the navbar above it, the popup sizes to its own content, which is
       what a button that is only as wide as a name wants. */
    .expo-user-menu__popup {
        width: calc(var(--sidebar-width) - 40px);
    }

    /* One tile to a row on a phone: three columns of 2.5rem figures do not fit 343px. */
    .expo-stat-row {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* ==========================================================================
   Exhibitor documents (/documents)
   ========================================================================== */

/* This page alone narrows the shared page body: the upload card and a short file list have no
   use for the full width of a desktop window, and a line of text that runs the whole way across
   is a line nobody finishes. Scoped with :has() on the marker the page renders, because
   .expo-page__body belongs to every screen — see the wrapper in Documents.razor. */
.expo-page__body:has(> .expo-documents-page) {
    max-width: 998px;
    width: 100%;
    margin-left: auto;
    margin-right: auto;
}

.expo-documents__upload {
    margin-bottom: 1rem;
}

.expo-documents__heading {
    margin: 0 0 12px;
    font-size: 1.125rem;
    font-weight: 700;
    color: var(--text-body);
}

/* The chooser. A <label> carrying the theme's button classes, so it is a button to look at and
   a file picker to use; these two declarations are only what the element itself still needs. */
.expo-documents__choose {
    display: inline-flex;
    cursor: pointer;
}

/* The drop target. Dashed because the dash is what says "put something here" — a solid border
   at this size reads as a panel, which is the one thing it must not look like. */
.expo-documents__drop {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 92px;
    margin: 12px 0 16px;
    padding: 20px;
    border: 2px dashed var(--kendo-color-border);
    border-radius: var(--kendo-border-radius-lg);
    background-color: var(--surface-muted);
    text-align: center;
    transition: border-color 0.15s ease, background-color 0.15s ease;
}

.expo-documents__drop-text {
    color: var(--kendo-color-subtle);
}

/* The file input itself, covering the box and invisible. This is what makes the whole area both
   droppable and clickable: a file input takes a dropped file natively, so the zone needs no drag
   handlers and no script. Kept in the accessibility tree — it is the real control, and the
   label above points at it. */
.expo-documents__input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
}

/* Dragging a file over the input is a hover as far as CSS is concerned, which is the one hook
   available without script — and it lights the zone at exactly the moment it matters. */
.expo-documents__drop:has(.expo-documents__input:hover) {
    border-color: var(--ats-orange);
    background-color: var(--surface-raised);
}

.expo-documents__drop:has(.expo-documents__input:focus-visible) {
    outline: 2px solid var(--expo-ink-navy);
    outline-offset: 2px;
}

/* Qualified past Telerik's `.k-body p`, which sets `margin: 0 0 var(--kendo-font-size)` at
   (0,1,1) and beats a bare class at (0,1,0) — see the note on .expo-empty above. Unqualified,
   the top margin here was simply discarded and a stray 16px appeared underneath. */
.k-body p.expo-documents__status {
    margin: 0.5rem 0 0;
    font-weight: 600;
    overflow-wrap: anywhere;
}

.expo-documents__status--error {
    color: var(--kendo-color-error);
}

.expo-documents__progress {
    margin-top: 10px;
    height: 6px;
    border-radius: var(--kendo-border-radius-full, 9999px);
    background-color: var(--kendo-color-base-subtle);
    overflow: hidden;
}

.expo-documents__progress-bar {
    height: 100%;
    background-color: var(--ats-orange);
    transition: width 0.2s ease;
}

/* The empty state, dashed to match the drop zone above it — the two are the same kind of
   placeholder, one waiting for a file and one reporting that none arrived. */
.k-body p.expo-documents__none {
    margin: 0;
    padding: 14px 16px;
    border: 1px dashed var(--kendo-color-border);
    border-radius: var(--kendo-border-radius-md);
    background-color: var(--surface-muted);
    color: var(--kendo-color-subtle);
}

.expo-documents__list {
    list-style: none;
    margin: 0;
    padding: 0;
}

/* Name, then size and date, then the actions — in a row where there is room for one, stacked
   where there is not. The name is the only part that gives up width, which is why it is the
   only one that truncates. */
.expo-documents__item {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 10px 0;
    border-bottom: 1px solid var(--kendo-color-border-alt);
}

.expo-documents__name {
    flex: 1 1 auto;
    min-width: 0;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.expo-documents__meta {
    flex: 0 0 auto;
    color: var(--kendo-color-subtle);
    font-size: 0.875rem;
}

.expo-documents__actions {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 4px;
}

@media (max-width: 767px) {
    /* Three things on one row is more than a phone has width for, and the name is the one that
       would be squeezed to nothing. */
    .expo-documents__item {
        flex-direction: column;
        align-items: stretch;
        gap: 4px;
    }

    .expo-documents__actions {
        justify-content: flex-end;
    }
}

/* ==========================================================================
   Lead sample badges (Settings)
   ========================================================================== */

.k-body p.expo-badge-samples__help {
    margin: 0 0 16px;
    color: var(--kendo-color-subtle);
}

/* Side by side where there is room, stacked where there is not — three barcodes are a set, and
   a scanner is pointed at one at a time either way. */
.expo-badge-samples {
    list-style: none;
    display: flex;
    flex-wrap: wrap;
    gap: 24px;
    margin: 0;
    padding: 0;
}

/* Each sample is the sidebar's ExpoMobile card (.expo-sidebar__card, .expo-sidebar__mobile):
   the white surface, the border-alt hairline, the 8px radius and elevation-1, 1rem of padding
   and a 0.625rem gap; the label as its eyebrow - small, bold, tracked uppercase in the subtle
   text colour; and the code framed the way that card frames its QR. Two QR codes on one
   product, one in the rail and three here, now read as the same object. */
.expo-badge-samples__item {
    display: flex;
    flex-direction: column;
    gap: 0.625rem;
    padding: 1rem;
    background-color: var(--kendo-color-surface);
    border: 1px solid var(--kendo-color-border-alt);
    border-radius: 8px;
    box-shadow: var(--kendo-elevation-1);
}

.expo-badge-samples__label {
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--kendo-color-subtle);
}

/* The ExpoMobile card's image frame - hairline, 6px radius, white behind it. The 10px of white
   inside the frame is kept from before: a reader needs a quiet zone around the code, and the
   white is what supplies it whatever the card is. Capped so a large QR does not take the whole
   panel. */
.expo-badge-samples__image {
    display: block;
    width: 220px;
    max-width: 100%;
    height: auto;
    padding: 10px;
    background-color: #fff;
    border: 1px solid var(--kendo-color-border-alt);
    border-radius: 6px;
}

/* ==========================================================================
   Attendance Traffic per Exhibitor — list beside its chart
   ========================================================================== */

/* Stacked by default. The two panels are siblings in flow below the split width, which is what
   every narrower screen gets. */
.expo-traffic-split {
    display: block;
    margin-top: 1rem;
}

@media (min-width: 1280px) {
    .expo-traffic-split {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 16px;

        /* Top-aligned rather than stretched: the chart is a fixed 420px and the list is as tall
           as its page of rows, and a stretched chart panel would leave a tall empty box under
           the chart itself. */
        align-items: start;
    }

    /* The panels' own 16px bottom margin is the grid's gap here, and it would otherwise add a
       second one under the row. */
    .expo-traffic-split > .expo-panel {
        margin-bottom: 0;
    }

    /* Nothing charted yet, so there is one panel and no second column to fill. Without this the
       list would sit in the left half with the right half empty for as long as the page is open
       — which is the arrangement the legacy had, and the reason it looked unfinished. */
    .expo-traffic-split:has(> .expo-panel:only-child) {
        grid-template-columns: 1fr;
    }
}

/* At full size the header lays out to roughly 550px of content, so this is where it stops
   fitting rather than a device width. Below it the app's own name is the first thing to go:
   whoever opened this on a phone knows which app they opened. It stays in the accessibility
   tree. */
@media (max-width: 639px) {
    .expo-navbar__title {
        position: absolute;
        width: 1px;
        height: 1px;
        margin: -1px;
        padding: 0;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }
}

/* Settings' panel headers wear the sidebar's surface, surface-alt, so the page's section titles
   read as the same material as the rail beside them.

   Last in the file and as specific as the rule that clears the fill from a panel showing an
   empty or loading message (.expo-panel:has(> .expo-panel__body > .expo-empty)), so a Lead
   Sample Badges panel with no samples keeps its band like the others. That rule also takes the
   header's side padding to zero to sit flush with a frameless message, which a filled band
   cannot do, so it is put back here at the exhibitor header's 16px. */
.expo-page .expo-settings .expo-panel > .expo-panel__header {
    background-color: var(--kendo-color-surface-alt);
}

.expo-page .expo-settings .expo-panel:has(> .expo-panel__body > :is(.expo-empty, .expo-loader)) > .expo-panel__header {
    padding-left: 16px;
    padding-right: 16px;
}

/* ==========================================================================
   Welcome banner - see App.razor
   ==========================================================================

   Floats over the page rather than taking space in it, by request: nothing is pushed down, so
   it covers the top of the bar on desktop and tablet and the foot of the screen on a phone
   until it is dismissed. Above the bar (4) and the rail's caret (5), below Telerik's popups and
   dialogs, which render into their own containers far higher.

   The darker ATS orange with white text, the pairing .expo-radio-card already uses. */
.expo-welcome {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 6;
    box-sizing: border-box;

    /* Room on the right for the close button, which is positioned against this box. */
    padding: 10px 52px 10px 16px;
    background-color: var(--ats-orange-dark);
    color: #fff;
    box-shadow: var(--kendo-elevation-2);
    font-size: 14px;
    font-weight: 600;
    line-height: 1.4;
    text-align: center;
}

/* At .k-body p's specificity, which otherwise gives the paragraph a bottom margin. */
.k-body p.expo-welcome__text {
    margin: 0;
}

.expo-welcome a {
    color: #fff;
    text-decoration: underline;
}

/* Navy is the app's focus ring, and navy on this orange is hard to find; white reads. */
.expo-welcome :focus-visible {
    outline-color: #fff;
}

.expo-welcome__dismiss {
    margin-left: 8px;
    padding: 2px 10px;
    border: 1px solid #fff;
    border-radius: var(--radius-sm);
    background-color: transparent;
    color: #fff;
    font: inherit;
    cursor: pointer;
}

.expo-welcome__dismiss:hover {
    background-color: #fff;
    color: var(--ats-orange-dark);
}

.expo-welcome__close {
    position: absolute;
    top: 50%;
    right: 8px;
    transform: translateY(-50%);
    width: 26px;
    height: 26px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background-color: transparent;
    color: #fff;
    font-size: 13px;
    line-height: 1;
    cursor: pointer;
}

.expo-welcome__close:hover {
    background-color: rgba(255, 255, 255, 0.2);
}

/* Across the foot of the screen on a phone, clear of the home indicator. */
@media (max-width: 767px) {
    .expo-welcome {
        top: auto;
        bottom: 0;
        padding-bottom: calc(10px + env(safe-area-inset-bottom));
        text-align: left;
    }

    /* On a phone the ✕ alone dismisses it, by request. */
    .expo-welcome__dismiss {
        display: none;
    }
}
