/* ==========================================================================
   VestaSys — application stylesheet (hand-written, no build step)
   ==========================================================================

   This file replaces the previous Tailwind CSS + DaisyUI setup that was
   compiled through npm/Vite. The application is deployed without Node, so the
   stylesheet is authored by hand and served straight from public/ via a plain
   <link> tag.

   The class NAMES are deliberately unchanged from the Tailwind/DaisyUI ones the
   367 Blade views already use (`btn`, `card`, `flex`, `text-sm`, `bg-base-100`,
   …). That keeps the markup untouched, so this migration cannot silently break
   a view: every class the views reference is implemented below.

   Layout of this file. ORDER IS LOAD-BEARING: components come before the
   utilities so a utility always wins against a component rule of the same
   specificity — `class="btn h-20"` must render a 5rem-tall button, and
   `sm:stats-horizontal` must beat `stats-vertical`. Adding a component rule
   below the utilities would silently break both. Sections are numbered by
   their original authoring order, which is why the numbers below are not
   sequential:
     1. Theme tokens (light / dark / skretting kiosk)
     2. Reset and base elements
    10. Components (btn, card, input, table, modal, …)
     3. Layout utilities (display, flex, grid, position)
     4. Spacing utilities (margin, padding, gap, space-y)
     5. Sizing utilities (width, height, min/max)
     6. Typography utilities
     7. Colour utilities (text, background, border)
     8. Border, radius, shadow and effect utilities
     9. Responsive and state variants (sm:, md:, lg:, hover:, focus:, print:)
    11. Skretting kiosk theme and the feeding animation

   Colour tokens are CSS custom properties so a theme swap is a token swap.
   Fractional colours (`text-base-content/60`) use color-mix() against the token
   rather than a hard-coded rgba, so they follow the active theme automatically.
   ========================================================================== */

/* ==========================================================================
   1. Theme tokens
   ========================================================================== */

/* Light theme — the default for the authenticated back-office UI. */
:root {
    --color-base-100: #ffffff;
    --color-base-200: #f3f4f6;
    --color-base-300: #e4e7ec;
    --color-base-content: #1b1f27;

    /* Skretting brand red, taken from the official logo SVG (#c8102e). */
    --color-primary: #c8102e;
    --color-primary-content: #ffffff;
    --color-secondary: #8f0a20;
    --color-secondary-content: #ffffff;
    --color-accent: #0d7c8c;
    --color-accent-content: #ffffff;
    --color-neutral: #2b303b;
    --color-neutral-content: #f5f6f8;

    --color-info: #0284c7;
    --color-info-content: #ffffff;
    --color-success: #15803d;
    --color-success-content: #ffffff;
    --color-warning: #b45309;
    --color-warning-content: #ffffff;
    --color-error: #c0182c;
    --color-error-content: #ffffff;

    --radius-selector: 1rem;
    --radius-field: 0.5rem;
    --radius-box: 0.75rem;

    /* Height of a default button, input and select.

       3rem is a touch target: correct for the wall-mounted kiosk and the phone
       check-in flow, which is why it is the base value. The authenticated
       back-office shell overrides it to 2.5rem further down — that UI is driven
       with a mouse and keyboard, and a toolbar of 48px controls wastes a lot of
       vertical space on a laptop. The size modifiers (btn-sm, btn-lg, input-sm,
       …) set explicit heights and are unaffected by either value. */
    --control-height: 3rem;

    --font-sans: 'Instrument Sans', ui-sans-serif, system-ui, -apple-system,
        'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif,
        'Apple Color Emoji', 'Segoe UI Emoji';
    --font-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas,
        'Liberation Mono', monospace;
}

/* Dark theme — selected by the employee's saved theme preference. */
[data-theme='dark'] {
    --color-base-100: #1d222b;
    --color-base-200: #171b22;
    --color-base-300: #11141a;
    --color-base-content: #e6e9ee;

    --color-primary: #e8324c;
    --color-primary-content: #ffffff;
    --color-secondary: #b21230;
    --color-secondary-content: #ffffff;
    --color-accent: #22a3b5;
    --color-accent-content: #06232a;
    --color-neutral: #11141a;
    --color-neutral-content: #e6e9ee;

    --color-info: #38bdf8;
    --color-info-content: #06212e;
    --color-success: #4ade80;
    --color-success-content: #05210f;
    --color-warning: #fbbf24;
    --color-warning-content: #241a02;
    --color-error: #f87171;
    --color-error-content: #2a0708;
}

/* Back-office density.

   Set on <body> by layouts/app.blade.php only. The authenticated shell is a
   mouse-and-keyboard UI where a row of 48px controls eats a lot of screen, so
   its default control height is 2.5rem. Every other layout — the kiosk, the
   mobile visitor flow, the public screens and the login page — keeps the 3rem
   touch height from :root, so nothing that is tapped with a finger shrinks. */
.app-shell { --control-height: 2.5rem; }

/* ==========================================================================
   2. Reset and base elements
   ========================================================================== */

*,
*::before,
*::after {
    box-sizing: border-box;
    border-width: 0;
    border-style: solid;
    border-color: var(--color-base-300);
}

* {
    margin: 0;
}

html {
    -webkit-text-size-adjust: 100%;
    tab-size: 4;
    line-height: 1.5;
}

body {
    min-height: 100vh;
    font-family: var(--font-sans);
    font-size: 1rem;
    line-height: 1.5;
    color: var(--color-base-content);
    background-color: var(--color-base-200);
    -webkit-font-smoothing: antialiased;
}

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

a {
    color: inherit;
    text-decoration: none;
}

img, svg, video, canvas, iframe {
    display: block;
    max-width: 100%;
}

img, svg, video {
    height: auto;
}

ul, ol {
    list-style: none;
    padding: 0;
}

button, input, select, textarea, optgroup {
    font: inherit;
    color: inherit;
    margin: 0;
    /* Browsers give <button> a default padding (~1px 6px). Left in place it
       offsets any bare button against neighbouring text — the sign-out button
       sat indented from the profile link beside it in the user menu. Component
       classes such as .btn set their own padding after this rule. */
    padding: 0;
}

button {
    background: none;
    cursor: pointer;
}

button:disabled {
    cursor: not-allowed;
}

table {
    border-collapse: collapse;
    width: 100%;
}

hr {
    border-top-width: 1px;
}

[hidden] {
    display: none !important;
}

/* Hide Alpine x-cloak elements until Alpine initialises. */
[x-cloak] {
    display: none !important;
}

/* ==========================================================================
   10. Components

   DaisyUI-equivalent component classes. These carry the visual identity of the
   back-office UI, so they are written against the theme tokens only.
   ========================================================================== */

/* ── Buttons ─────────────────────────────────────────────────────────────── */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    height: var(--control-height);
    min-height: var(--control-height);
    padding: 0 1rem;
    /* The neutral button carries its own surface and hairline.

       It used to be `background: var(--color-base-200)` with a transparent
       border — the exact colour of the page, since <body> is `bg-base-200`.
       Inside a card that read fine, but every neutral button placed on the page
       itself (the Edit action in a show view's header row, for one) rendered as
       bare text with no button shape until it was hovered. base-100 plus a
       base-300 hairline reads as a button on both surfaces and in both themes. */
    border: 1px solid var(--color-base-300);
    border-radius: var(--radius-field);
    background-color: var(--color-base-100);
    color: var(--color-base-content);
    font-size: 0.875rem;
    font-weight: 600;
    line-height: 1;
    text-align: center;
    white-space: nowrap;
    cursor: pointer;
    user-select: none;
    transition: background-color 150ms ease, border-color 150ms ease,
        color 150ms ease, box-shadow 150ms ease, transform 100ms ease;
}
.btn:hover { background-color: var(--color-base-200); }
.btn:active { transform: translateY(1px); }
.btn:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
.btn:disabled,
.btn-disabled {
    opacity: 0.5;
    pointer-events: none;
}

/* Filled variants clear the neutral button's hairline — a coloured fill is its
   own edge, and a base-300 border around a red button reads as a stray outline. */
.btn-primary { background-color: var(--color-primary); color: var(--color-primary-content); border-color: transparent; }
.btn-primary:hover { background-color: color-mix(in srgb, var(--color-primary) 85%, black); }
.btn-error { background-color: var(--color-error); color: var(--color-error-content); border-color: transparent; }
.btn-error:hover { background-color: color-mix(in srgb, var(--color-error) 85%, black); }
.btn-success { background-color: var(--color-success); color: var(--color-success-content); border-color: transparent; }
.btn-success:hover { background-color: color-mix(in srgb, var(--color-success) 85%, black); }
.btn-warning { background-color: var(--color-warning); color: var(--color-warning-content); border-color: transparent; }
.btn-warning:hover { background-color: color-mix(in srgb, var(--color-warning) 85%, black); }

.btn-ghost {
    background-color: transparent;
    border-color: transparent;
    color: inherit;
}
.btn-ghost:hover { background-color: color-mix(in srgb, var(--color-base-content) 10%, transparent); }

.btn-outline {
    background-color: transparent;
    border-color: currentColor;
}
.btn-outline:hover {
    background-color: var(--color-base-content);
    color: var(--color-base-100);
}
/* Outline pairings for every colour modifier.

   These are REQUIRED, not decorative. A colour modifier such as .btn-error sets
   a filled background plus its *content* colour for the label. .btn-outline then
   clears the background but cannot clear that label colour, so the text keeps a
   colour chosen to sit on the filled variant and becomes invisible against the
   page — `btn-outline btn-error` rendered white-on-white in the light theme, and
   `btn-outline btn-warning` near-black on the dark kiosk. Each pairing therefore
   has to restate the label colour as the variant's own colour, and restore the
   content colour only on hover, where the fill comes back. */
.btn-outline.btn-primary { color: var(--color-primary); border-color: var(--color-primary); }
.btn-outline.btn-primary:hover { background-color: var(--color-primary); color: var(--color-primary-content); }
.btn-outline.btn-error { color: var(--color-error); border-color: var(--color-error); }
.btn-outline.btn-error:hover { background-color: var(--color-error); color: var(--color-error-content); }
.btn-outline.btn-success { color: var(--color-success); border-color: var(--color-success); }
.btn-outline.btn-success:hover { background-color: var(--color-success); color: var(--color-success-content); }
.btn-outline.btn-warning { color: var(--color-warning); border-color: var(--color-warning); }
.btn-outline.btn-warning:hover { background-color: var(--color-warning); color: var(--color-warning-content); }

.btn-active { background-color: var(--color-base-300); }

.btn-xs { height: 1.5rem; min-height: 1.5rem; padding: 0 0.5rem; font-size: 0.6875rem; }
.btn-sm { height: 2rem; min-height: 2rem; padding: 0 0.75rem; font-size: 0.8125rem; }
.btn-lg { height: 4rem; min-height: 4rem; padding: 0 1.5rem; font-size: 1.125rem; }
.btn-block { display: flex; width: 100%; }
/* Square buttons follow the control height, or they stop being square as soon
   as the shell changes it. */
.btn-square { width: var(--control-height); padding: 0; }
.btn-square.btn-sm { width: 2rem; }
.btn-square.btn-xs { width: 1.5rem; }
.no-animation { transition: none; }

/* ── Cards ───────────────────────────────────────────────────────────────── */
.card {
    position: relative;
    display: flex;
    flex-direction: column;
    border-radius: var(--radius-box);
    background-color: var(--color-base-100);
}
.card-body {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    gap: 0.5rem;
    padding: 1.5rem;
}
.card-title {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 1.125rem;
    font-weight: 600;
}
.card-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 0.5rem;
}

/* ── Form controls ───────────────────────────────────────────────────────── */
.form-control {
    display: flex;
    flex-direction: column;
}
.label {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.25rem 0.125rem;
    user-select: none;
}
.label-text {
    font-size: 0.875rem;
    color: var(--color-base-content);
}
.label-text-alt {
    font-size: 0.75rem;
    color: color-mix(in srgb, var(--color-base-content) 60%, transparent);
}

/* Form controls are NOT full-width by default.

   The views were authored against DaisyUI, where these size to their content
   and `w-full` is added wherever full width is wanted — and it is, on ~230 of
   them. Forcing width:100% here instead made every toolbar filter claim a whole
   row, which broke the data-table's single-row toolbar into a stack. The ~80
   controls with no width class are almost all `input-sm`/`select-sm` filters
   that are meant to sit inline.

   Fields wrapped in `.form-control` (the form layout wrapper) do fill their
   column, which is what a stacked form wants and keeps the handful of bare
   `input input-bordered` form fields looking right. */
.input,
.select,
.textarea,
.file-input {
    display: block;
    height: var(--control-height);
    padding: 0 0.75rem;
    border: 1px solid transparent;
    border-radius: var(--radius-field);
    background-color: var(--color-base-100);
    color: var(--color-base-content);
    font-size: 0.875rem;
    line-height: 1.5;
    transition: border-color 150ms ease, box-shadow 150ms ease;
}
.textarea {
    height: auto;
    min-height: 5rem;
    padding: 0.625rem 0.75rem;
    line-height: 1.5;
}
.select {
    padding-right: 2rem;
    /* Native arrow replaced with an inline SVG so the control matches the theme. */
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23888' stroke-width='2'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.5rem center;
    background-size: 1rem;
    appearance: none;
}
.file-input { padding: 0.5rem 0.75rem; }
.file-input::file-selector-button {
    height: 2rem;
    margin-right: 0.75rem;
    padding: 0 0.75rem;
    border: 0;
    border-radius: var(--radius-field);
    background-color: var(--color-base-300);
    color: var(--color-base-content);
    font-size: 0.8125rem;
    font-weight: 600;
    cursor: pointer;
}

/* Inside a form layout wrapper, controls fill the column (see the note above). */
.form-control > .input,
.form-control > .select,
.form-control > .textarea,
.form-control > .file-input {
    width: 100%;
}

.input-bordered,
.select-bordered,
.textarea-bordered,
.file-input-bordered {
    border-color: color-mix(in srgb, var(--color-base-content) 20%, transparent);
}

.input:focus,
.select:focus,
.textarea:focus,
.file-input:focus {
    outline: none;
    border-color: var(--color-primary);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-primary) 25%, transparent);
}

.input-xs { height: 1.5rem; font-size: 0.6875rem; padding: 0 0.5rem; }
.input-sm, .file-input-sm { height: 2rem; font-size: 0.8125rem; padding: 0 0.5rem; }
/* A select keeps extra room on the trailing edge for its arrow. This must set
   the sides individually — sharing the `padding` shorthand above would reset
   the base rule's padding-right and let long option text run under the arrow. */
.select-sm {
    height: 2rem;
    font-size: 0.8125rem;
    padding-left: 0.5rem;
    padding-right: 1.75rem;
}
.textarea-sm { min-height: 4rem; font-size: 0.8125rem; }
.input-lg { height: 4rem; font-size: 1.125rem; padding: 0 1rem; }

/* Checkbox / radio / toggle — restyled native controls, so they stay accessible. */
.checkbox {
    flex-shrink: 0;
    width: 1.5rem;
    height: 1.5rem;
    border: 1px solid color-mix(in srgb, var(--color-base-content) 35%, transparent);
    border-radius: 0.25rem;
    background-color: var(--color-base-100);
    appearance: none;
    cursor: pointer;
}
.checkbox:checked {
    background-color: var(--color-base-content);
    border-color: var(--color-base-content);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='white' stroke-width='2.5'%3E%3Cpath d='M3 8.5l3.5 3.5L13 5'/%3E%3C/svg%3E");
    background-size: contain;
}
.checkbox-primary:checked { background-color: var(--color-primary); border-color: var(--color-primary); }
.checkbox-warning:checked { background-color: var(--color-warning); border-color: var(--color-warning); }
.checkbox-xs { width: 1rem; height: 1rem; }
.checkbox-sm { width: 1.25rem; height: 1.25rem; }
.checkbox-lg { width: 2rem; height: 2rem; }

.radio {
    flex-shrink: 0;
    width: 1.5rem;
    height: 1.5rem;
    border: 1px solid color-mix(in srgb, var(--color-base-content) 35%, transparent);
    border-radius: 9999px;
    appearance: none;
    cursor: pointer;
}
.radio:checked {
    border-width: 6px;
    border-color: var(--color-base-content);
}
.radio-primary:checked { border-color: var(--color-primary); }

.toggle {
    flex-shrink: 0;
    position: relative;
    width: 3rem;
    height: 1.5rem;
    border: 1px solid color-mix(in srgb, var(--color-base-content) 35%, transparent);
    border-radius: 9999px;
    background-color: color-mix(in srgb, var(--color-base-content) 20%, transparent);
    appearance: none;
    cursor: pointer;
    transition: background-color 150ms ease;
}
.toggle::before {
    content: '';
    position: absolute;
    top: 50%;
    left: 0.125rem;
    width: 1.125rem;
    height: 1.125rem;
    border-radius: 9999px;
    background-color: #ffffff;
    transform: translateY(-50%);
    transition: left 150ms ease;
}
.toggle:checked::before { left: calc(100% - 1.25rem); }
.toggle-primary:checked { background-color: var(--color-primary); border-color: var(--color-primary); }
.toggle-warning:checked { background-color: var(--color-warning); border-color: var(--color-warning); }

/* ── Badges ──────────────────────────────────────────────────────────────── */
.badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.25rem;
    height: 1.5rem;
    padding: 0 0.625rem;
    border: 1px solid transparent;
    border-radius: 9999px;
    background-color: var(--color-base-200);
    color: var(--color-base-content);
    font-size: 0.75rem;
    font-weight: 500;
    white-space: nowrap;
}
.badge-xs { height: 0.875rem; padding: 0 0.375rem; font-size: 0.625rem; }
.badge-sm { height: 1.25rem; padding: 0 0.5rem; font-size: 0.6875rem; }
.badge-lg { height: 1.75rem; padding: 0 0.75rem; font-size: 0.875rem; }
.badge-ghost { background-color: color-mix(in srgb, var(--color-base-content) 8%, transparent); }
.badge-outline { background-color: transparent; border-color: currentColor; }
.badge-primary { background-color: var(--color-primary); color: var(--color-primary-content); }
.badge-accent { background-color: var(--color-accent); color: var(--color-accent-content); }
.badge-info { background-color: var(--color-info); color: var(--color-info-content); }
.badge-success { background-color: var(--color-success); color: var(--color-success-content); }
.badge-warning { background-color: var(--color-warning); color: var(--color-warning-content); }
.badge-error { background-color: var(--color-error); color: var(--color-error-content); }
.badge-neutral { background-color: var(--color-neutral); color: var(--color-neutral-content); }

/* ── Alerts ──────────────────────────────────────────────────────────────── */
.alert {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.875rem 1rem;
    border: 1px solid color-mix(in srgb, var(--color-base-content) 12%, transparent);
    border-radius: var(--radius-box);
    background-color: var(--color-base-200);
    font-size: 0.875rem;
}
.alert-info {
    background-color: color-mix(in srgb, var(--color-info) 12%, var(--color-base-100));
    border-color: color-mix(in srgb, var(--color-info) 35%, transparent);
}
.alert-success {
    background-color: color-mix(in srgb, var(--color-success) 12%, var(--color-base-100));
    border-color: color-mix(in srgb, var(--color-success) 35%, transparent);
}
.alert-warning {
    background-color: color-mix(in srgb, var(--color-warning) 14%, var(--color-base-100));
    border-color: color-mix(in srgb, var(--color-warning) 35%, transparent);
}
.alert-error {
    background-color: color-mix(in srgb, var(--color-error) 12%, var(--color-base-100));
    border-color: color-mix(in srgb, var(--color-error) 35%, transparent);
}

/* ── Tables ──────────────────────────────────────────────────────────────── */
.table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    font-size: 0.875rem;
    text-align: left;
}
.table th,
.table td {
    padding: 0.75rem 1rem;
    vertical-align: middle;
    border-bottom: 1px solid var(--color-base-200);
}
.table th {
    font-size: 0.75rem;
    font-weight: 600;
    color: color-mix(in srgb, var(--color-base-content) 60%, transparent);
    white-space: nowrap;
}
.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr:hover { background-color: color-mix(in srgb, var(--color-base-content) 4%, transparent); }
.table-xs th, .table-xs td { padding: 0.25rem 0.5rem; font-size: 0.75rem; }
.table-sm th, .table-sm td { padding: 0.5rem 0.75rem; }

/* ── Breadcrumbs ─────────────────────────────────────────────────────────── */
.breadcrumbs {
    max-width: 100%;
    overflow-x: auto;
    padding: 0.5rem 0;
    font-size: 0.875rem;
}
.breadcrumbs > ul {
    display: flex;
    align-items: center;
    white-space: nowrap;
    min-height: min-content;
}
.breadcrumbs > ul > li { display: flex; align-items: center; }
.breadcrumbs > ul > li + li::before {
    content: '';
    display: block;
    width: 0.375rem;
    height: 0.375rem;
    margin: 0 0.5rem;
    border-top: 1px solid currentColor;
    border-right: 1px solid currentColor;
    opacity: 0.4;
    transform: rotate(45deg);
}

/* ── Menu (sidebar navigation) ───────────────────────────────────────────── */
.menu {
    display: flex;
    flex-direction: column;
    /* NOT flex-wrap: wrap. On a column flex container, wrapping spills overflow
       into additional COLUMNS rather than scrolling, which pushed the tail of
       the sidebar out to the right of the panel. The sidebar sets its own
       overflow-y, which is what should handle a long nav. */
    flex-wrap: nowrap;
    padding: 0.5rem;
    font-size: 0.875rem;
}

/* Nested submenus (the <ul> inside a collapsible group) stack and sit indented
   under their heading. */
.menu ul {
    display: flex;
    flex-direction: column;
    margin-left: 0.75rem;
    padding-left: 0.5rem;
    border-left: 1px solid color-mix(in srgb, var(--color-base-content) 12%, transparent);
}

/* Interactive rows. `<details>` is excluded because it is only a wrapper — its
   <summary> is the actual row and is matched separately, or the icon and label
   inside it would never be laid out and the group would render as a stack. */
.menu li > *:not(ul):not(details):not(.menu-title),
.menu li > details > summary {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    border-radius: var(--radius-field);
    text-align: left;
    cursor: pointer;
    transition: background-color 150ms ease;
}
.menu li > *:not(ul):not(details):not(.menu-title):hover,
.menu li > details > summary:hover {
    background-color: color-mix(in srgb, var(--color-base-content) 8%, transparent);
}
.menu li > *.active,
.menu li > *.menu-active {
    background-color: var(--color-primary);
    color: var(--color-primary-content);
}

/* Hovering the current page must not wash it out.

   The generic row-hover rule above is the more specific selector — its three
   :not() arguments outweigh `.menu li > *.menu-active` — so hovering the active
   row replaced the primary fill with a translucent grey while the label kept
   its primary-content colour. In the light theme that is white text on a
   near-white background, i.e. unreadable. These selectors repeat the hover
   rule's shape so they outrank it, and keep the brand fill (just darkened for
   feedback) rather than dropping to the plain row-hover look, which would also
   have thrown away the "you are here" cue for as long as the pointer rests on
   it. Darkening the fill keeps the contrast right in both themes. */
.menu li > *:not(ul):not(details):not(.menu-title).active:hover,
.menu li > *:not(ul):not(details):not(.menu-title).menu-active:hover {
    background-color: color-mix(in srgb, var(--color-primary) 85%, black);
    color: var(--color-primary-content);
}

/* Replace the native disclosure triangle with a chevron on the trailing edge,
   so a group heading reads like the rest of the nav rows. */
.menu li > details > summary {
    list-style: none;
}
.menu li > details > summary::-webkit-details-marker {
    display: none;
}
.menu li > details > summary::after {
    content: '';
    width: 0.4rem;
    height: 0.4rem;
    margin-left: auto;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    opacity: 0.5;
    transform: rotate(-45deg);
    transition: transform 150ms ease;
}
.menu li > details[open] > summary::after {
    transform: rotate(45deg);
}
.menu-sm li > *:not(ul):not(.menu-title) { padding: 0.375rem 0.625rem; font-size: 0.8125rem; }

/* ── Sidebar rail (the collapsed navigation) ─────────────────────────────────

   The desktop collapse used to hide the sidebar outright, which cost the user
   every cue about where they were in the app. Collapsed now means a rail: the
   icons stay put and keep their active highlight, while the labels, the group
   chevrons and the submenus step aside. app.js reveals the label as a floating
   tooltip on hover — a CSS ::after tooltip like `.tooltip` above cannot be used
   here, because the sidebar is its own vertical scroll container and would clip
   anything drawn outside its edge.

   The state lives on <html>, not on the sidebar, so the inline script in the
   layout's <head> can restore it before the first paint — putting it on the
   sidebar itself meant the full-width nav painted first and visibly snapped to
   the rail a moment later. The width selector carries the sidebar's id so it
   beats the `w-64` utility the markup carries, whatever the rule order. */
.sidebar-rail #app-sidebar { width: 4rem; }

.sidebar-rail .menu { padding: 0.5rem 0.375rem; }

/* Labels are hidden visually but stay in the accessibility tree — `display:none`
   would strip them, leaving each row as an icon with no accessible name. */
.sidebar-rail .menu li > a > span,
.sidebar-rail .menu li > details > summary > span {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* A submenu and the credit line have nowhere to go in a 4rem column. Opening a
   group is handled in app.js instead: it expands the sidebar back to full width. */
.sidebar-rail .menu li > details > ul,
.sidebar-rail .sidebar-credits { display: none; }
.sidebar-rail .menu li > details > summary::after { content: none; }

/* Centre what is left of each row. */
.sidebar-rail .menu li > *:not(ul):not(details):not(.menu-title),
.sidebar-rail .menu li > details > summary {
    justify-content: center;
    padding-left: 0;
    padding-right: 0;
}

/* When the current page sits inside a group, the highlighted child is hidden in
   the collapsed submenu — so the group's own icon takes the highlight. Scoped to
   the rail: an expanded sidebar shows the open group and its active child, and
   lighting up both would read as two selections. */
.sidebar-rail .menu li > details > summary.menu-group-active {
    background-color: var(--color-primary);
    color: var(--color-primary-content);
}
/* Same darken-on-hover as an active row, for the same reason. */
.sidebar-rail .menu li > details > summary.menu-group-active:hover {
    background-color: color-mix(in srgb, var(--color-primary) 85%, black);
    color: var(--color-primary-content);
}

/* The floating rail label. Fixed rather than absolute so the sidebar's own
   overflow cannot clip it; positioned by app.js against the hovered row. */
.sidebar-tooltip {
    position: fixed;
    z-index: 60;
    padding: 0.25rem 0.5rem;
    border-radius: 0.375rem;
    background-color: var(--color-neutral);
    color: var(--color-neutral-content);
    font-size: 0.75rem;
    white-space: nowrap;
    pointer-events: none;
    opacity: 0;
    transition: opacity 120ms ease;
}
.sidebar-tooltip.is-visible { opacity: 1; }

/* ── Modal ───────────────────────────────────────────────────────────────── */
/* Modals are HIDDEN by default and revealed by `.modal-open`, which the views
   toggle from Alpine (`:class="{ 'modal-open': open }"`). Without this a modal
   would render as a permanent full-screen overlay covering the page. */
.modal {
    position: fixed;
    inset: 0;
    display: none;
    place-items: center;
    z-index: 200;
    padding: 1rem;
    background-color: rgba(0, 0, 0, 0.45);
    overflow-y: auto;
}
.modal-open,
.modal[open] {
    display: grid;
}
.modal-box {
    position: relative;
    width: 100%;
    max-width: 32rem;
    max-height: calc(100vh - 5rem);
    padding: 1.5rem;
    border-radius: var(--radius-box);
    background-color: var(--color-base-100);
    box-shadow: 0 20px 40px rgba(0, 0, 0, 0.25);
    overflow-y: auto;
}
.modal-action {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
    margin-top: 1.5rem;
}
.modal-backdrop { position: fixed; inset: 0; z-index: -1; }

/* ── Dropdown ────────────────────────────────────────────────────────────── */
/* Dropdowns are CLOSED by default and opened by one of two triggers, matching
   the two markup patterns in the app:

     1. `.dropdown-open` on the wrapper, toggled by Alpine (the navbar user menu).
     2. `<details class="dropdown">` with a `<summary>` trigger (the data-table
        export menu, payroll), opened by the native `[open]` attribute.

   Deliberately NOT `:focus-within`. Focus cannot express a toggle: clicking the
   trigger a second time leaves it focused, so a focus-driven menu can only ever
   open, never close. Any new dropdown should drive `.dropdown-open` from Alpine
   rather than relying on focus.

   Hiding uses visibility/opacity rather than `display`, deliberately: the
   content element is usually also a `.menu` (display: flex), and toggling
   `display` here would fight that rule. `visibility: hidden` still removes the
   menu from hit-testing and the accessibility tree, so a closed menu cannot be
   clicked or tabbed into. */
.dropdown { position: relative; display: inline-flex; }
.dropdown-content {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 50;
    border-radius: var(--radius-box);
    background-color: var(--color-base-100);
    box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);
    visibility: hidden;
    opacity: 0;
    transform: translateY(-0.25rem);
    transition: opacity 150ms ease, transform 150ms ease, visibility 150ms;
}
.dropdown.dropdown-open > .dropdown-content,
.dropdown[open] > .dropdown-content {
    visibility: visible;
    opacity: 1;
    transform: translateY(0);
}
.dropdown-end .dropdown-content { left: auto; right: 0; }

/* ── Tabs ────────────────────────────────────────────────────────────────── */
.tabs { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0.25rem; }
.tab {
    display: inline-flex;
    align-items: center;
    height: 2.5rem;
    padding: 0 1rem;
    border-radius: var(--radius-field);
    font-size: 0.875rem;
    color: color-mix(in srgb, var(--color-base-content) 65%, transparent);
    cursor: pointer;
}
.tab-active { background-color: var(--color-base-100); color: var(--color-base-content); font-weight: 600; }
.tabs-boxed { padding: 0.25rem; border-radius: var(--radius-box); background-color: var(--color-base-200); }

/* ── Stats ───────────────────────────────────────────────────────────────── */
.stats {
    display: grid;
    grid-auto-flow: column;
    border-radius: var(--radius-box);
    background-color: var(--color-base-100);
    overflow-x: auto;
}
.stats-vertical { grid-auto-flow: row; }
.stat {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 0.25rem 1rem;
    padding: 1rem 1.5rem;
    border-top: 1px solid var(--color-base-200);
}
.stats-vertical > .stat:first-child,
.stats:not(.stats-vertical) > .stat { border-top: 0; }
.stat-title {
    grid-column: 1;
    font-size: 0.75rem;
    color: color-mix(in srgb, var(--color-base-content) 60%, transparent);
}
.stat-value {
    grid-column: 1;
    font-size: 1.875rem;
    font-weight: 700;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
}
.stat-desc {
    grid-column: 1;
    font-size: 0.75rem;
    color: color-mix(in srgb, var(--color-base-content) 55%, transparent);
}
.stat-figure { grid-column: 2; grid-row: 1 / span 3; align-self: center; }

/* ── Progress ────────────────────────────────────────────────────────────── */
.progress {
    width: 100%;
    height: 0.5rem;
    border-radius: 9999px;
    background-color: color-mix(in srgb, var(--color-base-content) 15%, transparent);
    appearance: none;
    overflow: hidden;
}
.progress::-webkit-progress-bar { background-color: transparent; }
.progress::-webkit-progress-value { background-color: var(--color-base-content); border-radius: 9999px; }
.progress::-moz-progress-bar { background-color: var(--color-base-content); }
.progress-primary::-webkit-progress-value { background-color: var(--color-primary); }
.progress-primary::-moz-progress-bar { background-color: var(--color-primary); }
.progress-success::-webkit-progress-value { background-color: var(--color-success); }
.progress-success::-moz-progress-bar { background-color: var(--color-success); }
.progress-warning::-webkit-progress-value { background-color: var(--color-warning); }
.progress-warning::-moz-progress-bar { background-color: var(--color-warning); }
.progress-error::-webkit-progress-value { background-color: var(--color-error); }
.progress-error::-moz-progress-bar { background-color: var(--color-error); }

/* ── Join (segmented control) ────────────────────────────────────────────── */
.join { display: inline-flex; align-items: stretch; }
.join-item { border-radius: 0; }
.join > .join-item:first-child { border-top-left-radius: var(--radius-field); border-bottom-left-radius: var(--radius-field); }
.join > .join-item:last-child { border-top-right-radius: var(--radius-field); border-bottom-right-radius: var(--radius-field); }

/* ── Divider ─────────────────────────────────────────────────────────────── */
.divider {
    display: flex;
    align-items: center;
    margin: 0.75rem 0;
    font-size: 0.8125rem;
    color: color-mix(in srgb, var(--color-base-content) 55%, transparent);
}
.divider::before,
.divider::after {
    content: '';
    flex: 1 1 0;
    height: 1px;
    background-color: var(--color-base-300);
}
.divider::before { margin-right: 0.75rem; }
.divider::after { margin-left: 0.75rem; }

/* ── Avatar ──────────────────────────────────────────────────────────────── */
.avatar { display: inline-flex; }
.avatar > div { overflow: hidden; }
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar-placeholder > div {
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: var(--color-neutral);
    color: var(--color-neutral-content);
}

/* ── Timeline ────────────────────────────────────────────────────────────── */
.timeline { display: grid; }
.timeline-vertical { grid-auto-flow: row; }
.timeline-compact { gap: 0.25rem; }
.timeline-start { grid-column: 1; }
.timeline-middle { grid-column: 2; }
.timeline-end { grid-column: 3; }

/* ── Tooltip ─────────────────────────────────────────────────────────────── */
.tooltip { position: relative; }
.tooltip::after {
    content: attr(data-tip);
    position: absolute;
    z-index: 60;
    max-width: 16rem;
    padding: 0.25rem 0.5rem;
    border-radius: 0.25rem;
    background-color: var(--color-neutral);
    color: var(--color-neutral-content);
    font-size: 0.75rem;
    font-weight: 400;
    white-space: normal;
    width: max-content;
    opacity: 0;
    pointer-events: none;
    transition: opacity 150ms ease;
}
/* Reveal on hover AND focus — <x-help-tip> is focusable, so keyboard users must
   be able to read the tip too. */
.tooltip:hover::after,
.tooltip:focus::after,
.tooltip:focus-visible::after { opacity: 1; }

/* Direction variants, emitted by <x-help-tip> as tooltip-{top|bottom|left|right}. */
.tooltip::after,
.tooltip-top::after {
    bottom: 100%; left: 50%;
    transform: translate(-50%, -0.25rem);
}
.tooltip-bottom::after {
    top: 100%; bottom: auto; left: 50%;
    transform: translate(-50%, 0.25rem);
}
.tooltip-left::after {
    right: 100%; left: auto; top: 50%; bottom: auto;
    transform: translate(-0.25rem, -50%);
}
.tooltip-right::after {
    left: 100%; right: auto; top: 50%; bottom: auto;
    transform: translate(0.25rem, -50%);
}

/* ── Links ───────────────────────────────────────────────────────────────── */
.link { text-decoration-line: underline; cursor: pointer; }
.link-hover { text-decoration-line: none; }
.link-hover:hover { text-decoration-line: underline; }
.link-primary { color: var(--color-primary); }

/* ── Loading spinner ─────────────────────────────────────────────────────── */
.loading {
    display: inline-block;
    width: 1.5rem;
    height: 1.5rem;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: 9999px;
    animation: spin 0.6s linear infinite;
}
/* `.loading` already renders as a spinner; `.loading-spinner` exists so the
   class the submit-loading script injects (public/js/app.js) is a real rule
   rather than a silent no-op. */
.loading-spinner { border-radius: 9999px; }
.loading-sm { width: 1rem; height: 1rem; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ==========================================================================
   3. Layout utilities
   ========================================================================== */

.block { display: block; }
.inline-block { display: inline-block; }
.inline { display: inline; }
.flex { display: flex; }
.inline-flex { display: inline-flex; }
.grid { display: grid; }
.hidden { display: none; }

.flex-row { flex-direction: row; }
.flex-col { flex-direction: column; }
.flex-wrap { flex-wrap: wrap; }
.flex-1 { flex: 1 1 0%; }
.flex-none { flex: none; }
.grow { flex-grow: 1; }
.shrink-0 { flex-shrink: 0; }

.items-start { align-items: flex-start; }
.items-center { align-items: center; }
.items-end { align-items: flex-end; }
.items-stretch { align-items: stretch; }
.self-center { align-self: center; }

.justify-start { justify-content: flex-start; }
.justify-center { justify-content: center; }
.justify-end { justify-content: flex-end; }
.justify-between { justify-content: space-between; }

.grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-cols-7 { grid-template-columns: repeat(7, minmax(0, 1fr)); }
.grid-cols-8 { grid-template-columns: repeat(8, minmax(0, 1fr)); }
.grid-cols-9 { grid-template-columns: repeat(9, minmax(0, 1fr)); }

/* Arbitrary-value grid templates used by the detail/definition layouts. */
.grid-cols-\[9rem_1fr\] { grid-template-columns: 9rem 1fr; }
.grid-cols-\[10rem_1fr\] { grid-template-columns: 10rem 1fr; }
.grid-cols-\[12rem_1fr\] { grid-template-columns: 12rem 1fr; }
.grid-cols-\[9rem_1fr_9rem_1fr\] { grid-template-columns: 9rem 1fr 9rem 1fr; }
.grid-cols-\[1fr_14rem_auto\] { grid-template-columns: 1fr 14rem auto; }

.col-span-2 { grid-column: span 2 / span 2; }

.static { position: static; }
.relative { position: relative; }
.absolute { position: absolute; }
.fixed { position: fixed; }
.sticky { position: sticky; }

.inset-0 { inset: 0; }
.inset-x-0 { left: 0; right: 0; }
.inset-y-0 { top: 0; bottom: 0; }
.top-0 { top: 0; }
.top-16 { top: 4rem; }
.right-0 { right: 0; }
.bottom-0 { bottom: 0; }
.left-0 { left: 0; }
.left-\[-5\%\] { left: -5%; }

.z-0 { z-index: 0; }
.-z-10 { z-index: -10; }
.z-10 { z-index: 10; }
.z-30 { z-index: 30; }
.z-40 { z-index: 40; }
.z-\[200\] { z-index: 200; }

.overflow-hidden { overflow: hidden; }
.overflow-x-auto { overflow-x: auto; }
.overflow-y-auto { overflow-y: auto; }

.object-cover { object-fit: cover; }
.object-contain { object-fit: contain; }

.aspect-video { aspect-ratio: 16 / 9; }

.float-left { float: left; }

.pointer-events-none { pointer-events: none; }
.select-none { user-select: none; }
.cursor-pointer { cursor: pointer; }
.cursor-help { cursor: help; }

/* ==========================================================================
   4. Spacing utilities
   ========================================================================== */

.p-0 { padding: 0; }
.p-1 { padding: 0.25rem; }
.p-1\.5 { padding: 0.375rem; }
.p-2 { padding: 0.5rem; }
.p-3 { padding: 0.75rem; }
.p-4 { padding: 1rem; }
.p-6 { padding: 1.5rem; }
.p-7 { padding: 1.75rem; }
.p-8 { padding: 2rem; }
.p-10 { padding: 2.5rem; }

.px-0 { padding-left: 0; padding-right: 0; }
.px-1 { padding-left: 0.25rem; padding-right: 0.25rem; }
.px-3 { padding-left: 0.75rem; padding-right: 0.75rem; }
.px-4 { padding-left: 1rem; padding-right: 1rem; }
.px-5 { padding-left: 1.25rem; padding-right: 1.25rem; }
.px-6 { padding-left: 1.5rem; padding-right: 1.5rem; }
.px-8 { padding-left: 2rem; padding-right: 2rem; }

.py-0 { padding-top: 0; padding-bottom: 0; }
.py-1 { padding-top: 0.25rem; padding-bottom: 0.25rem; }
.py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; }
.py-3 { padding-top: 0.75rem; padding-bottom: 0.75rem; }
.py-4 { padding-top: 1rem; padding-bottom: 1rem; }
.py-5 { padding-top: 1.25rem; padding-bottom: 1.25rem; }
.py-6 { padding-top: 1.5rem; padding-bottom: 1.5rem; }
.py-8 { padding-top: 2rem; padding-bottom: 2rem; }
.py-10 { padding-top: 2.5rem; padding-bottom: 2.5rem; }
.py-12 { padding-top: 3rem; padding-bottom: 3rem; }
.py-16 { padding-top: 4rem; padding-bottom: 4rem; }

.pt-1 { padding-top: 0.25rem; }
.pt-2\.5 { padding-top: 0.625rem; }
.pt-3 { padding-top: 0.75rem; }
.pt-4 { padding-top: 1rem; }
.pt-5 { padding-top: 1.25rem; }
.pt-6 { padding-top: 1.5rem; }
.pb-1 { padding-bottom: 0.25rem; }
.pb-3 { padding-bottom: 0.75rem; }
.pb-4 { padding-bottom: 1rem; }
.pb-6 { padding-bottom: 1.5rem; }
.pl-3 { padding-left: 0.75rem; }
.pl-5 { padding-left: 1.25rem; }
.pr-3 { padding-right: 0.75rem; }

.m-0 { margin: 0; }
.mx-auto { margin-left: auto; margin-right: auto; }
.my-1 { margin-top: 0.25rem; margin-bottom: 0.25rem; }
.my-2 { margin-top: 0.5rem; margin-bottom: 0.5rem; }

.mt-0\.5 { margin-top: 0.125rem; }
.mt-1 { margin-top: 0.25rem; }
.mt-2 { margin-top: 0.5rem; }
.mt-3 { margin-top: 0.75rem; }
.mt-4 { margin-top: 1rem; }
.mt-5 { margin-top: 1.25rem; }
.mt-6 { margin-top: 1.5rem; }
.mt-8 { margin-top: 2rem; }
.mt-auto { margin-top: auto; }
.-mt-2 { margin-top: -0.5rem; }

.mb-1 { margin-bottom: 0.25rem; }
.mb-2 { margin-bottom: 0.5rem; }
.mb-3 { margin-bottom: 0.75rem; }
.mb-4 { margin-bottom: 1rem; }
.mb-5 { margin-bottom: 1.25rem; }
.mb-6 { margin-bottom: 1.5rem; }

.ml-1 { margin-left: 0.25rem; }
.ml-2 { margin-left: 0.5rem; }
.ml-3 { margin-left: 0.75rem; }
.ml-auto { margin-left: auto; }
.mr-3 { margin-right: 0.75rem; }

.gap-0\.5 { gap: 0.125rem; }
.gap-1 { gap: 0.25rem; }
.gap-1\.5 { gap: 0.375rem; }
.gap-2 { gap: 0.5rem; }
.gap-2\.5 { gap: 0.625rem; }
.gap-3 { gap: 0.75rem; }
.gap-4 { gap: 1rem; }
.gap-5 { gap: 1.25rem; }
.gap-6 { gap: 1.5rem; }
.gap-8 { gap: 2rem; }
.gap-x-4 { column-gap: 1rem; }
.gap-x-6 { column-gap: 1.5rem; }
.gap-y-1 { row-gap: 0.25rem; }
.gap-y-2 { row-gap: 0.5rem; }
.gap-y-3 { row-gap: 0.75rem; }

/* space-y-* puts the gap between siblings, matching Tailwind's behaviour. */
.space-y-1 > * + * { margin-top: 0.25rem; }
.space-y-1\.5 > * + * { margin-top: 0.375rem; }
.space-y-2 > * + * { margin-top: 0.5rem; }
.space-y-3 > * + * { margin-top: 0.75rem; }
.space-y-4 > * + * { margin-top: 1rem; }
.space-y-6 > * + * { margin-top: 1.5rem; }

.divide-y > * + * { border-top-width: 1px; }

/* ==========================================================================
   5. Sizing utilities
   ========================================================================== */

.w-0 { width: 0; }
.w-3 { width: 0.75rem; }
.w-3\.5 { width: 0.875rem; }
.w-4 { width: 1rem; }
.w-5 { width: 1.25rem; }
.w-6 { width: 1.5rem; }
.w-8 { width: 2rem; }
.w-9 { width: 2.25rem; }
.w-10 { width: 2.5rem; }
.w-12 { width: 3rem; }
.w-14 { width: 3.5rem; }
.w-16 { width: 4rem; }
.w-18 { width: 4.5rem; }
.w-20 { width: 5rem; }
.w-24 { width: 6rem; }
.w-28 { width: 7rem; }
.w-32 { width: 8rem; }
.w-36 { width: 9rem; }
.w-40 { width: 10rem; }
.w-44 { width: 11rem; }
.w-48 { width: 12rem; }
.w-64 { width: 16rem; }
.w-auto { width: auto; }
.w-full { width: 100%; }
.w-screen { width: 100vw; }
.w-fit { width: fit-content; }
.w-\[18px\] { width: 18px; }
.w-\[110\%\] { width: 110%; }
.w-\[276px\] { width: 276px; }

.h-1 { height: 0.25rem; }
.h-3 { height: 0.75rem; }
.h-3\.5 { height: 0.875rem; }
.h-4 { height: 1rem; }
.h-5 { height: 1.25rem; }
.h-6 { height: 1.5rem; }
.h-8 { height: 2rem; }
.h-10 { height: 2.5rem; }
.h-11 { height: 2.75rem; }
.h-12 { height: 3rem; }
.h-14 { height: 3.5rem; }
.h-16 { height: 4rem; }
.h-20 { height: 5rem; }
.h-24 { height: 6rem; }
.h-40 { height: 10rem; }
.h-auto { height: auto; }
.h-full { height: 100%; }
.h-screen { height: 100vh; }
.h-\[18px\] { height: 18px; }
.h-\[72px\] { height: 72px; }
.h-\[16vh\] { height: 16vh; }

.min-w-0 { min-width: 0; }
.min-w-40 { min-width: 10rem; }
.min-w-\[44rem\] { min-width: 44rem; }

.min-h-16 { min-height: 4rem; }
.min-h-screen { min-height: 100vh; }
.min-h-\[88px\] { min-height: 88px; }
.min-h-\[200px\] { min-height: 200px; }
.min-h-\[4\.75rem\] { min-height: 4.75rem; }

.max-w-xs { max-width: 20rem; }
.max-w-sm { max-width: 24rem; }
.max-w-md { max-width: 28rem; }
.max-w-lg { max-width: 32rem; }
.max-w-xl { max-width: 36rem; }
.max-w-2xl { max-width: 42rem; }
.max-w-3xl { max-width: 48rem; }
.max-w-4xl { max-width: 56rem; }
.max-w-5xl { max-width: 64rem; }
.max-w-\[10rem\] { max-width: 10rem; }
.max-w-\[160px\] { max-width: 160px; }
.max-w-\[220px\] { max-width: 220px; }
.max-w-\[64\%\] { max-width: 64%; }
.max-w-\[92\%\] { max-width: 92%; }

.max-h-64 { max-height: 16rem; }
.max-h-72 { max-height: 18rem; }

/* ==========================================================================
   6. Typography utilities
   ========================================================================== */

.text-\[10px\] { font-size: 10px; line-height: 1.4; }
.text-\[11px\] { font-size: 11px; line-height: 1.45; }
.text-xs { font-size: 0.75rem; line-height: 1rem; }
.text-sm { font-size: 0.875rem; line-height: 1.25rem; }
.text-base { font-size: 1rem; line-height: 1.5rem; }
.text-lg { font-size: 1.125rem; line-height: 1.75rem; }
.text-xl { font-size: 1.25rem; line-height: 1.75rem; }
.text-2xl { font-size: 1.5rem; line-height: 2rem; }
.text-3xl { font-size: 1.875rem; line-height: 2.25rem; }
.text-4xl { font-size: 2.25rem; line-height: 2.5rem; }
.text-5xl { font-size: 3rem; line-height: 1.1; }
.text-6xl { font-size: 3.75rem; line-height: 1.05; }
.text-7xl { font-size: 4.5rem; line-height: 1.05; }
.text-8xl { font-size: 6rem; line-height: 1; }
.text-\[8vmin\] { font-size: 8vmin; line-height: 1.1; }

/* Montserrat (the kiosk display face) is self-hosted at 200/300/400/600 only —
   see public/css/fonts.css. Prefer those weights on kiosk headings so the
   browser renders a real cut instead of synthesising a faux-bold. */
.font-thin { font-weight: 200; }
.font-light { font-weight: 300; }
.font-normal { font-weight: 400; }
.font-medium { font-weight: 500; }
.font-semibold { font-weight: 600; }
.font-bold { font-weight: 700; }
.font-extrabold { font-weight: 800; }
.font-black { font-weight: 900; }

.font-mono { font-family: var(--font-mono); }
.tabular-nums { font-variant-numeric: tabular-nums; }

.text-left { text-align: left; }
.text-center { text-align: center; }
.text-right { text-align: right; }

.uppercase { text-transform: uppercase; }
.italic { font-style: italic; }
.underline { text-decoration-line: underline; }

.leading-tight { line-height: 1.25; }
.leading-relaxed { line-height: 1.625; }

.tracking-tight { letter-spacing: -0.015em; }
.tracking-wide { letter-spacing: 0.025em; }
.tracking-wider { letter-spacing: 0.05em; }
.tracking-widest { letter-spacing: 0.1em; }
.tracking-\[0\.2em\] { letter-spacing: 0.2em; }
.tracking-\[0\.25em\] { letter-spacing: 0.25em; }
.tracking-\[0\.3em\] { letter-spacing: 0.3em; }
.tracking-\[0\.35em\] { letter-spacing: 0.35em; }
.tracking-\[0\.4em\] { letter-spacing: 0.4em; }
.tracking-\[0\.5em\] { letter-spacing: 0.5em; }

.truncate {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.whitespace-nowrap { white-space: nowrap; }
.whitespace-pre-line { white-space: pre-line; }
.break-all { word-break: break-all; }

.list-disc { list-style-type: disc; }
.list-decimal { list-style-type: decimal; }
.list-inside { list-style-position: inside; }

.align-middle { vertical-align: middle; }
.align-top { vertical-align: top; }

/* ==========================================================================
   7. Colour utilities

   Fractional opacities use color-mix() so they track the active theme token
   instead of baking in a fixed rgba value.
   ========================================================================== */

.text-base-content { color: var(--color-base-content); }
.text-base-content\/80 { color: color-mix(in srgb, var(--color-base-content) 80%, transparent); }
.text-base-content\/70 { color: color-mix(in srgb, var(--color-base-content) 70%, transparent); }
.text-base-content\/60 { color: color-mix(in srgb, var(--color-base-content) 60%, transparent); }
.text-base-content\/50 { color: color-mix(in srgb, var(--color-base-content) 50%, transparent); }
.text-base-content\/40 { color: color-mix(in srgb, var(--color-base-content) 40%, transparent); }
.text-base-content\/30 { color: color-mix(in srgb, var(--color-base-content) 30%, transparent); }

.text-primary { color: var(--color-primary); }
.text-primary\/80 { color: color-mix(in srgb, var(--color-primary) 80%, transparent); }
.text-neutral-content { color: var(--color-neutral-content); }
.text-error { color: var(--color-error); }
.text-error-content { color: var(--color-error-content); }
.text-success { color: var(--color-success); }
.text-warning { color: var(--color-warning); }
.text-info { color: var(--color-info); }
.text-white { color: #ffffff; }
.text-white\/60 { color: rgba(255, 255, 255, 0.6); }
.text-white\/40 { color: rgba(255, 255, 255, 0.4); }

.bg-base-100 { background-color: var(--color-base-100); }
.bg-base-100\/50 { background-color: color-mix(in srgb, var(--color-base-100) 50%, transparent); }
.bg-base-200 { background-color: var(--color-base-200); }
.bg-base-200\/60 { background-color: color-mix(in srgb, var(--color-base-200) 60%, transparent); }
.bg-base-200\/50 { background-color: color-mix(in srgb, var(--color-base-200) 50%, transparent); }
.bg-base-200\/40 { background-color: color-mix(in srgb, var(--color-base-200) 40%, transparent); }
.bg-base-300 { background-color: var(--color-base-300); }
.bg-base-300\/85 { background-color: color-mix(in srgb, var(--color-base-300) 85%, transparent); }
.bg-base-300\/50 { background-color: color-mix(in srgb, var(--color-base-300) 50%, transparent); }
.bg-base-300\/40 { background-color: color-mix(in srgb, var(--color-base-300) 40%, transparent); }

.bg-primary { background-color: var(--color-primary); }
.bg-primary\/10 { background-color: color-mix(in srgb, var(--color-primary) 10%, transparent); }
.bg-neutral { background-color: var(--color-neutral); }
.bg-error { background-color: var(--color-error); }
.bg-error\/15 { background-color: color-mix(in srgb, var(--color-error) 15%, transparent); }
.bg-error\/5 { background-color: color-mix(in srgb, var(--color-error) 5%, transparent); }
.bg-success\/30 { background-color: color-mix(in srgb, var(--color-success) 30%, transparent); }
.bg-success\/15 { background-color: color-mix(in srgb, var(--color-success) 15%, transparent); }
.bg-success\/10 { background-color: color-mix(in srgb, var(--color-success) 10%, transparent); }
.bg-warning\/40 { background-color: color-mix(in srgb, var(--color-warning) 40%, transparent); }
.bg-warning\/15 { background-color: color-mix(in srgb, var(--color-warning) 15%, transparent); }
.bg-white { background-color: #ffffff; }
.bg-white\/10 { background-color: rgba(255, 255, 255, 0.1); }
.bg-white\/5 { background-color: rgba(255, 255, 255, 0.05); }
.bg-black\/80 { background-color: rgba(0, 0, 0, 0.8); }
.bg-black\/40 { background-color: rgba(0, 0, 0, 0.4); }

.border-base-100 { border-color: var(--color-base-100); }
.border-base-100\/40 { border-color: color-mix(in srgb, var(--color-base-100) 40%, transparent); }
.border-base-200 { border-color: var(--color-base-200); }
.border-base-300 { border-color: var(--color-base-300); }
.border-primary { border-color: var(--color-primary); }
.border-primary\/20 { border-color: color-mix(in srgb, var(--color-primary) 20%, transparent); }
.border-error\/40 { border-color: color-mix(in srgb, var(--color-error) 40%, transparent); }
.border-error\/30 { border-color: color-mix(in srgb, var(--color-error) 30%, transparent); }
.border-white\/15 { border-color: rgba(255, 255, 255, 0.15); }
.border-white\/10 { border-color: rgba(255, 255, 255, 0.1); }
.border-transparent { border-color: transparent; }

.divide-base-200 > * + * { border-color: var(--color-base-200); }

/* ==========================================================================
   8. Border, radius, shadow and effect utilities
   ========================================================================== */

.border { border-width: 1px; }
.border-0 { border-width: 0; }
.border-t { border-top-width: 1px; }
.border-t-2 { border-top-width: 2px; }
.border-b { border-bottom-width: 1px; }
.border-l-2 { border-left-width: 2px; }
.border-r { border-right-width: 1px; }
.border-r-2 { border-right-width: 2px; }
.border-dashed { border-style: dashed; }
.last\:border-0:last-child { border-width: 0; }

.rounded { border-radius: 0.25rem; }
.rounded-sm { border-radius: 0.125rem; }
.rounded-lg { border-radius: 0.5rem; }
.rounded-xl { border-radius: 0.75rem; }
.rounded-2xl { border-radius: 1rem; }
.rounded-full { border-radius: 9999px; }
.rounded-box { border-radius: var(--radius-box); }

.shadow {
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06), 0 1px 3px rgba(0, 0, 0, 0.08);
}
.shadow-lg {
    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}
.shadow-xl {
    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 20px 25px -5px rgba(0, 0, 0, 0.12);
}

/* `ring-inset` draws the ring inside the element's bounds, so a highlighted
   table cell doesn't overlap its neighbours. */
.ring-1 { box-shadow: 0 0 0 1px var(--ring-color, var(--color-primary)); }
.ring-2 { box-shadow: 0 0 0 2px var(--ring-color, var(--color-primary)); }
.ring-2.ring-inset { box-shadow: inset 0 0 0 2px var(--ring-color, var(--color-primary)); }
.ring-primary { --ring-color: var(--color-primary); }

.opacity-30 { opacity: 0.3; }
.opacity-40 { opacity: 0.4; }
.opacity-50 { opacity: 0.5; }
.opacity-60 { opacity: 0.6; }
.opacity-80 { opacity: 0.8; }
.opacity-100 { opacity: 1; }

.backdrop-blur { backdrop-filter: blur(8px); }
.backdrop-blur-sm { backdrop-filter: blur(4px); }
.backdrop-blur-md { backdrop-filter: blur(12px); }

.transition {
    transition-property: color, background-color, border-color, opacity, box-shadow, transform;
    transition-duration: 150ms;
    transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
.transition-all {
    transition-property: all;
    transition-duration: 150ms;
    transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
.transition-transform {
    transition-property: transform;
    transition-duration: 150ms;
    transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
.duration-200 { transition-duration: 200ms; }

.-translate-x-full { transform: translateX(-100%); }
.rotate-180 { transform: rotate(180deg); }

/* ==========================================================================
   9. Responsive and state variants
   ========================================================================== */

.hover\:bg-base-200:hover { background-color: var(--color-base-200); }
.hover\:bg-primary\/10:hover { background-color: color-mix(in srgb, var(--color-primary) 10%, transparent); }
.hover\:border-primary:hover { border-color: var(--color-primary); }
.hover\:border-primary\/40:hover { border-color: color-mix(in srgb, var(--color-primary) 40%, transparent); }
.hover\:text-primary:hover { color: var(--color-primary); }
.hover\:text-base-content\/70:hover { color: color-mix(in srgb, var(--color-base-content) 70%, transparent); }
.hover\:underline:hover { text-decoration-line: underline; }
.hover\:opacity-90:hover { opacity: 0.9; }
.hover\:shadow-md:hover {
    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
}
.hover\:shadow-\[0_12px_40px_rgba\(244\,101\,29\,0\.2\)\]:hover {
    box-shadow: 0 12px 40px color-mix(in srgb, var(--color-primary) 20%, transparent);
}
.hover\:-translate-y-0\.5:hover { transform: translateY(-0.125rem); }
.hover\:-translate-y-1:hover { transform: translateY(-0.25rem); }

.focus\:border-primary:focus { border-color: var(--color-primary); }
.focus\:outline-none:focus { outline: 2px solid transparent; outline-offset: 2px; }
.focus-within\:border-primary:focus-within { border-color: var(--color-primary); }

.active\:scale-95:active { transform: scale(0.95); }

@media (min-width: 640px) {
    .sm\:block { display: block; }
    .sm\:flex-row { flex-direction: row; }
    .sm\:items-center { align-items: center; }
    .sm\:justify-between { justify-content: space-between; }
    .sm\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .sm\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .sm\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
    .sm\:grid-cols-\[10rem_1fr\] { grid-template-columns: 10rem 1fr; }
    .sm\:grid-cols-\[12rem_1fr\] { grid-template-columns: 12rem 1fr; }
    .sm\:col-span-2 { grid-column: span 2 / span 2; }
    .sm\:w-56 { width: 14rem; }
    .sm\:max-w-3xl { max-width: 48rem; }
    .sm\:p-6 { padding: 1.5rem; }
    .sm\:p-10 { padding: 2.5rem; }
    .sm\:px-6 { padding-left: 1.5rem; padding-right: 1.5rem; }
    .sm\:px-10 { padding-left: 2.5rem; padding-right: 2.5rem; }
    .sm\:pt-8 { padding-top: 2rem; }
    .sm\:text-base { font-size: 1rem; line-height: 1.5rem; }
    .sm\:text-7xl { font-size: 4.5rem; line-height: 1.05; }
    .sm\:stats-horizontal { grid-auto-flow: column; }
}

@media (min-width: 768px) {
    .md\:col-span-2 { grid-column: span 2 / span 2; }
    .md\:col-span-3 { grid-column: span 3 / span 3; }
    .md\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .md\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .md\:grid-cols-\[9rem_1fr_9rem_1fr\] { grid-template-columns: 9rem 1fr 9rem 1fr; }
    .md\:grid-cols-\[1fr_14rem_auto\] { grid-template-columns: 1fr 14rem auto; }
}

@media (min-width: 1024px) {
    .lg\:hidden { display: none; }
    .lg\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .lg\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .lg\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
    .lg\:col-span-1 { grid-column: span 1 / span 1; }
    .lg\:col-span-2 { grid-column: span 2 / span 2; }
    .lg\:col-span-3 { grid-column: span 3 / span 3; }
    .lg\:sticky { position: sticky; }
    .lg\:top-16 { top: 4rem; }
    .lg\:top-20 { top: 5rem; }
    .lg\:z-auto { z-index: auto; }
    .lg\:translate-x-0 { transform: translateX(0); }
    .lg\:shadow-none { box-shadow: none; }
    .lg\:h-\[calc\(100vh-4rem\)\] { height: calc(100vh - 4rem); }
}

@media print {
    .print\:hidden { display: none !important; }
}

/* ==========================================================================
   11. Skretting kiosk theme and the feeding animation

   The kiosk / visitor screens run full-screen on wall-mounted displays and
   phones, and carry Skretting's brand rather than the back-office chrome.
   Applied via data-theme="skretting" on the kiosk and public layouts.

   Skretting makes aquaculture feed, so the ambient background is a feeding
   scene: feed pellets sink through the water and fish rise to meet and eat
   them. The feeding is the focal point — the fish exist to chase the pellets,
   not merely to swim past.
   ========================================================================== */

[data-theme='skretting'] {
    color-scheme: dark;

    /* Deep near-black red, so the brand red stays the brightest thing on screen. */
    --color-base-100: #1a0508;
    --color-base-200: #120406;
    --color-base-300: #080203;
    --color-base-content: #f5e9eb;

    --color-primary: #c8102e;   /* Skretting brand red (from the logo SVG) */
    --color-primary-content: #ffffff;
    --color-secondary: #8f0a20;
    --color-secondary-content: #ffffff;
    --color-accent: #f0b323;    /* feed-pellet gold — the "food" accent */
    --color-accent-content: #241a02;
    --color-neutral: #200609;
    --color-neutral-content: #f5e9eb;

    --color-info: #7fd3e6;
    --color-info-content: #05121a;
    --color-success: #4ade80;
    --color-success-content: #05210f;
    --color-warning: #fbbf24;
    --color-warning-content: #241a02;
    /* A distinct alarm red so errors never read as the brand red. */
    --color-error: #ff5c5c;
    --color-error-content: #2a0708;

    --radius-selector: 1rem;
    --radius-field: 0.75rem;
    --radius-box: 1rem;
}

/* The kiosk display face. Loaded as a self-hosted webfont (see fonts.css); the
   markup's own weight/size utilities still win because only the family is set. */
[data-theme='skretting'] :is(h1, h2, .kiosk-display) {
    font-family: 'Montserrat', var(--font-sans);
    letter-spacing: -0.01em;
}

/* ── Ambient water ───────────────────────────────────────────────────────── */

/* Slow-drifting colour pools that give the water depth. */
.feed-caustic {
    position: absolute;
    border-radius: 50%;
    filter: blur(80px);
    animation: feed-caustic-move 26s ease-in-out infinite;
}
.feed-caustic-1 {
    width: 60vw; height: 60vw; top: -22%; left: -12%;
    background: radial-gradient(circle, rgba(143, 10, 32, 0.5), transparent 70%);
}
.feed-caustic-2 {
    width: 55vw; height: 55vw; bottom: -28%; right: -14%;
    background: radial-gradient(circle, rgba(240, 179, 35, 0.12), transparent 70%);
    animation-duration: 32s;
    animation-delay: -9s;
}

.feed-bubble {
    position: absolute;
    bottom: -5vh;
    border-radius: 50%;
    background: radial-gradient(circle at 30% 30%, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.04));
    animation: feed-rise linear infinite;
}

/* ── Feed pellets ────────────────────────────────────────────────────────── */

/* A pellet sinks from above the viewport to the sea floor. The wrapper handles
   the fall; the inner dot handles the sway, so the two never fight over the
   transform property. */
.feed-pellet-track {
    position: absolute;
    top: 0;
    animation: feed-sink linear infinite;
}
.feed-pellet {
    display: block;
    border-radius: 50%;
    background: radial-gradient(circle at 35% 30%, #ffd97a, var(--color-accent) 60%, #a97708);
    box-shadow: 0 0 10px rgba(240, 179, 35, 0.45);
    animation: feed-drift ease-in-out infinite alternate;
}

/* The moment a fish reaches a pellet: a short gold flash where it is eaten. */
.feed-bite {
    position: absolute;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 50%;
    background: radial-gradient(circle, rgba(240, 179, 35, 0.55), transparent 65%);
    opacity: 0;
    animation: feed-bite-flash ease-out infinite;
}

/* ── Feeding fish ────────────────────────────────────────────────────────── */

/* Each fish is an SVG mask filled with currentColor, so a single asset themes
   to any brand colour. The nested tracks compose three motions without
   conflict: lunge (horizontal dash toward food), rise (vertical approach) and
   the tail-end bite nod. */
.feed-fish {
    display: block;
    aspect-ratio: 1;
    background-color: currentColor;
    -webkit-mask: url('/images/fish.svg') center / contain no-repeat;
    mask: url('/images/fish.svg') center / contain no-repeat;
}
.feed-fish-lunge {
    position: absolute;
    left: 0;
    animation: feed-lunge ease-in-out infinite;
}
.feed-fish-lunge-reverse { animation-name: feed-lunge-reverse; }
.feed-fish-rise { animation: feed-rise-to-food ease-in-out infinite alternate; }
.feed-fish-bite { animation: feed-bite-nod ease-in-out infinite; }

.feed-fish-brand  { color: var(--color-primary); opacity: 0.6; filter: drop-shadow(0 0 26px rgba(200, 16, 46, 0.4)); }
.feed-fish-deep   { color: var(--color-secondary); opacity: 0.42; }
.feed-fish-ghost  { color: #ffffff; opacity: 0.07; }
/* The fish artwork faces LEFT, so left-to-right swimmers are mirrored to face
   the direction they travel. */
.feed-fish-flip   { transform: scaleX(-1); }

/* ── Sea floor ───────────────────────────────────────────────────────────── */
.feed-wave path { fill: rgba(45, 8, 16, 0.7); }
.feed-wave-front path { fill: rgba(26, 5, 8, 0.85); }
.feed-wave-back  { animation: feed-wave-sway 18s ease-in-out infinite; opacity: 0.6; }
.feed-wave-front { animation: feed-wave-sway 12s ease-in-out infinite reverse; }

/* ── Keyframes ───────────────────────────────────────────────────────────── */

@keyframes feed-rise {
    0%   { transform: translate(0, 0); opacity: 0; }
    10%  { opacity: 0.5; }
    90%  { opacity: 0.3; }
    100% { transform: translate(24px, -105vh); opacity: 0; }
}
@keyframes feed-caustic-move {
    0%, 100% { transform: translate(-5%, -5%) scale(1); opacity: 0.5; }
    50%      { transform: translate(5%, 5%) scale(1.15); opacity: 0.85; }
}

/* Pellet falls the full height and fades as it is consumed near the bottom. */
@keyframes feed-sink {
    0%   { transform: translateY(-12vh); opacity: 0; }
    8%   { opacity: 1; }
    82%  { opacity: 1; }
    100% { transform: translateY(104vh); opacity: 0; }
}
@keyframes feed-drift {
    from { transform: translateX(-14px) rotate(-25deg); }
    to   { transform: translateX(14px) rotate(25deg); }
}
@keyframes feed-bite-flash {
    0%, 70%  { opacity: 0; transform: scale(0.4); }
    78%      { opacity: 1; transform: scale(1); }
    100%     { opacity: 0; transform: scale(1.6); }
}

/* Fish crosses the screen, pausing mid-way where the food is — the dwell in the
   middle of the timeline is what reads as "feeding" rather than "commuting". */
@keyframes feed-lunge {
    0%       { transform: translateX(-24vw); }
    38%      { transform: translateX(38vw); }
    52%      { transform: translateX(46vw); }
    100%     { transform: translateX(124vw); }
}
@keyframes feed-lunge-reverse {
    0%       { transform: translateX(124vw); }
    38%      { transform: translateX(56vw); }
    52%      { transform: translateX(48vw); }
    100%     { transform: translateX(-24vw); }
}
@keyframes feed-rise-to-food {
    from { transform: translateY(0) rotate(-6deg); }
    to   { transform: translateY(-34px) rotate(6deg); }
}
/* Quick snap of the head as the pellet is taken. */
@keyframes feed-bite-nod {
    0%, 44%, 60%, 100% { transform: rotate(0deg) scale(1); }
    50%                { transform: rotate(-9deg) scale(1.06); }
    55%                { transform: rotate(4deg) scale(1.02); }
}
@keyframes feed-wave-sway {
    0%, 100% { transform: translateX(0); }
    50%      { transform: translateX(-3%); }
}

/* Respect reduced-motion: freeze the ambient background entirely. */
@media (prefers-reduced-motion: reduce) {
    .feed-caustic, .feed-bubble, .feed-pellet-track, .feed-pellet, .feed-bite,
    .feed-fish-lunge, .feed-fish-rise, .feed-fish-bite,
    .feed-wave-back, .feed-wave-front {
        animation: none !important;
    }
}
