/* app.css - supplemental utility classes not included in styles.css bundle */

/* ── Semantic text colours ──────────────────────────────────────────────── */
.text-success  { color: var(--color-green-500);  }
.text-warning  { color: var(--color-yellow-500); }
.text-info     { color: var(--color-violet-600); }
.text-secondary { color: var(--secondary-foreground); }
.text-purple-600 { color: var(--color-violet-600); }

/* ── text-muted-foreground opacity variants ─────────────────────────────── */
.text-muted-foreground\/30 { color: color-mix(in oklab, var(--muted-foreground) 30%, transparent); }
.text-muted-foreground\/40 { color: color-mix(in oklab, var(--muted-foreground) 40%, transparent); }
.text-muted-foreground\/70 { color: color-mix(in oklab, var(--muted-foreground) 70%, transparent); }

/* ── Semantic background colours ────────────────────────────────────────── */
.bg-success  { background-color: var(--color-green-500);  }
.bg-warning  { background-color: var(--color-yellow-500); }
.bg-info     { background-color: var(--color-violet-600); }
.bg-card     { background-color: var(--card); }
.bg-current  { background-color: currentColor; }
.bg-secondary { background-color: var(--secondary); }
.bg-purple-100 { background-color: var(--color-violet-100); }
.bg-black\/50  { background-color: color-mix(in oklab, black 50%, transparent); }

/* ── Semantic bg opacity variants ───────────────────────────────────────── */
.bg-success\/5   { background-color: color-mix(in oklab, var(--color-green-500)  5%,  transparent); }
.bg-success\/10  { background-color: color-mix(in oklab, var(--color-green-500)  10%, transparent); }
.bg-success\/20  { background-color: color-mix(in oklab, var(--color-green-500)  20%, transparent); }
.bg-warning\/5   { background-color: color-mix(in oklab, var(--color-yellow-500) 5%,  transparent); }
.bg-warning\/10  { background-color: color-mix(in oklab, var(--color-yellow-500) 10%, transparent); }
.bg-info\/5      { background-color: color-mix(in oklab, var(--color-violet-600) 5%,  transparent); }
.bg-info\/10     { background-color: color-mix(in oklab, var(--color-violet-600) 10%, transparent); }
.bg-muted\/20    { background-color: color-mix(in oklab, var(--muted)            20%, transparent); }
.bg-secondary\/10 { background-color: color-mix(in oklab, var(--secondary)       10%, transparent); }

/* ── Border colour utilities ────────────────────────────────────────────── */
.border-success\/30     { border-color: color-mix(in oklab, var(--color-green-500)  30%, transparent); }
.border-success\/40     { border-color: color-mix(in oklab, var(--color-green-500)  40%, transparent); }
.border-warning\/30     { border-color: color-mix(in oklab, var(--color-yellow-500) 30%, transparent); }
.border-info\/20        { border-color: color-mix(in oklab, var(--color-violet-600) 20%, transparent); }
.border-destructive\/30 { border-color: color-mix(in oklab, var(--destructive)      30%, transparent); }
.border-primary\/40     { border-color: color-mix(in oklab, var(--primary)          40%, transparent); }

/* ── Opacity utilities ──────────────────────────────────────────────────── */
.opacity-30 { opacity: 0.3; }
.opacity-40 { opacity: 0.4; }
.opacity-70 { opacity: 0.7; }

/* ── Hover state utilities ──────────────────────────────────────────────── */
@media (hover: hover) {
  .hover\:bg-accent\/40:hover      { background-color: color-mix(in oklab, var(--accent)              40%, transparent); }
  .hover\:bg-amber-50:hover        { background-color: #fffbeb; }
  .hover\:bg-destructive:hover     { background-color: var(--destructive); }
  .hover\:bg-destructive\/10:hover { background-color: color-mix(in oklab, var(--destructive)         10%, transparent); }
  .hover\:bg-muted:hover           { background-color: var(--muted); }
  .hover\:bg-muted\/30:hover       { background-color: color-mix(in oklab, var(--muted)               30%, transparent); }
  .hover\:bg-success\/10:hover     { background-color: color-mix(in oklab, var(--color-green-500)     10%, transparent); }
  .hover\:bg-warning\/10:hover     { background-color: color-mix(in oklab, var(--color-yellow-500)    10%, transparent); }
  .hover\:text-primary-foreground:hover { color: var(--primary-foreground); }
}

/* ── Dark mode utilities ────────────────────────────────────────────────── */
.dark\:bg-card:is(.dark *) { background-color: var(--card); }
.dark\:flex:is(.dark *)    { display: flex; }

@media (hover: hover) {
  .dark\:hover\:bg-amber-900\/20:is(.dark *):hover {
    background-color: color-mix(in oklab, #78350f 20%, transparent);
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Utilities the pre-compiled Metronic bundle never emitted
   ───────────────────────────────────────────────────────────────────────────
   styles.css is a *shipped build* of Tailwind, not Tailwind itself: it
   contains only the utilities the Metronic demos happened to use. It has
   .lg:grid-cols-5 but no .lg:grid-cols-4, .sm:grid-cols-4 but no
   .sm:grid-cols-3, and no arbitrary-value utilities at all. A class it never
   emitted resolves to no rule and the element renders unstyled - silently.
   Found by asking the browser which of the templates' 276 class literals no
   loaded stylesheet defines. Anything added to a template that is not in the
   bundle belongs here.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Layout variables the markup references ─────────────────────────────── */
/* The bundle scopes its own --header-height to .demo1, which this app does
   not use; these mirror its values (70px desktop / 60px below lg). */
:root { --kt-header-height: 70px; }
@media (width < 64rem) { :root { --kt-header-height: 60px; } }
.h-\[--kt-header-height\] { height: var(--kt-header-height); }

/* ── Grid ───────────────────────────────────────────────────────────────── */
@media (width >= 40rem) {
  .sm\:col-span-2 { grid-column: span 2 / span 2; }
  .sm\:block      { display: block; }
}
@media (width >= 64rem) {
  .lg\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
.gap-x-6 { column-gap: calc(var(--spacing) * 6); }
.gap-y-4 { row-gap:    calc(var(--spacing) * 4); }

/* ── Saved places ───────────────────────────────────────────────────────── */
/* The disabled "Add a place" at the limit has to look disabled; without these
   two it rendered as a live primary button that did nothing. */
.opacity-50         { opacity: 0.5; }
.cursor-not-allowed { cursor: not-allowed; }
/* A saved address can be one long unbroken token (a plus code, a pasted
   URL); it wraps inside its card rather than widening it. */
.break-words        { overflow-wrap: break-word; }
.max-w-md           { max-width: 28rem; }

/* ── Spacing ────────────────────────────────────────────────────────────── */
.ms-6     { margin-inline-start: calc(var(--spacing) * 6); }
.mt-0\.5  { margin-top:          calc(var(--spacing) * 0.5); }
.pb-8     { padding-bottom:      calc(var(--spacing) * 8); }
.py-12    { padding-block:       calc(var(--spacing) * 12); }
.py-14    { padding-block:       calc(var(--spacing) * 14); }

/* ── Sizing ─────────────────────────────────────────────────────────────── */
.max-h-8          { max-height: calc(var(--spacing) * 8); }
.max-w-\[120px\]  { max-width:  120px; }
.max-w-\[150px\]  { max-width:  150px; }
.min-w-\[640px\]  { min-width:  640px; }
.min-h-screen     { min-height: 100vh; }
.max-h-64         { max-height: calc(var(--spacing) * 64); }
.basis-full       { flex-basis: 100%; }  /* the hire form's trip warning, a row of its own */

/* ── Typography ─────────────────────────────────────────────────────────── */
.font-bold           { font-weight: 700; }
.font-mono           { font-family: var(--font-mono); }
.list-disc           { list-style-type: disc; }
.whitespace-pre-line { white-space: pre-line; }

/* ── Box / overflow / stacking ──────────────────────────────────────────── */
.object-contain  { object-fit: contain; }
.overflow-x-auto { overflow-x: auto; }
.overflow-y-auto { overflow-y: auto; }
.top-full        { top: 100%; }
.z-50            { z-index: 50; }

/* ── Colour ─────────────────────────────────────────────────────────────── */
.bg-destructive\/10 { background-color: color-mix(in oklab, var(--destructive) 10%, transparent); }
.bg-primary\/20     { background-color: color-mix(in oklab, var(--primary)     20%, transparent); }

/* ── Shadow (same variable chain the bundle composes with) ──────────────── */
.shadow-lg {
  --tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}

/* ── Transition (matches the bundle's .transition timing variables) ─────── */
.transition-colors {
  transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke;
  transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
  transition-duration: var(--tw-duration, var(--default-transition-duration));
}

/* ── Variants ───────────────────────────────────────────────────────────── */
.last\:border-0:last-child { border-width: 0; }
/* Metronic's light: variant - the theme class sits on <html>, so this is the
   mirror of .dark\:flex above and must outrank a bare .hidden on the same
   element (the theme-toggle icons are "light:flex hidden" / "dark:flex hidden"). */
html:not(.dark) .light\:flex { display: flex; }
@media (hover: hover) {
  .hover\:underline:hover { text-decoration-line: underline; }
}

/* ── Intentionally style-free ───────────────────────────────────────────── */
/* .footer carries no rules by design: it is a DOM anchor kept for parity with
   the core app's markup, and the client suite allowlists it. Do not "fix" it
   by inventing styles - give it rules only if a real style is wanted.
   (.kt-sidebar-footer was in this note too until the phone-widths block below
   gave it a real rule, sizing its Sign Out button to 44px.) */
/* .leaflet-control-container and .leaflet-proxy are Leaflet's own, added by
   its script to every map; the vendored stylesheet styles their children
   only. Allowlisted in the client suite for the same reason. */

/* ═══════════════════════════════════════════════════════════════════════════
   Phone widths
   ───────────────────────────────────────────────────────────────────────────
   Measured at 375x667 and 320x568, the same pitfalls hostel-management hit
   and fixed. Before this block: 6 of 8 pages were wider than a 375 screen and
   8 of 8 wider than a 320 one, the browser zoomed out to 0.75 shrinking every
   control with it, the stretched header put the user menu off the right edge
   so Sign Out could not be reached, and the drawer's footer sat at y=756 on a
   667-tall screen.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Cards are flex and grid items, so min-width:auto sizes them to their widest
   row - a table - instead of their column. The table then scrolls inside its
   own card, which is where the sideways scrolling belongs. The layout
   wrappers carry min-w-0 in app.blade.php for the same reason. */
.kt-card { min-width: 0; }
.kt-card-content:has(> table) { overflow-x: auto; }

/* Utilities the templates need that the bundle never emitted. */
@media (width >= 64rem) {
  .lg\:px-7 { padding-inline: calc(var(--spacing) * 7); }
}

@media (width < 64rem) {
  /* Below lg the sidebar is a drawer, and these are the controls a thumb has
     to hit. 44px is the WCAG 2.5.5 / Apple HIG minimum; the build's icon
     buttons are 36px and its small ones 32px. The drawer toggle is sized in
     the template because it only exists below lg; these two also appear on
     the desktop header, which keeps the build's sizes. */
  #header button[title="Toggle theme"] { width: 2.75rem; height: 2.75rem; }
  .kt-sidebar-footer form button { width: 2.75rem; height: 2.75rem; }

  /* Saved places. A chip is the quickest way to set a point on a phone - it
     replaces a search and a pin - so it gets a full target there, as do the
     label suggestions and the edit / remove buttons on each place. */
  [data-saved-place], [data-label-suggestion], [data-save-suggestion] { min-height: 2.75rem; }
  [data-place-edit], [data-place-remove] { width: 2.75rem; height: 2.75rem; }

  /* At 44px a wrapping row of chips costs a line per two or three places, and
     it sits above every map on the form - three rows of it with a stop added.
     One row that scrolls sideways stays one row at any count; the chip cut
     off at the edge is the cue that there are more. The row is a stretched
     item of a column, so its own overflow never reaches the page width. */
  [data-saved-row] { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; padding-block: 1px; }
  [data-saved-row]::-webkit-scrollbar { display: none; }
  [data-saved-row] > * { flex-shrink: 0; }

  /* The drawer's menu is taller than a phone and was shrink-0, so the footer -
     name, email and Sign Out - sat below the bottom of a fixed panel that
     cannot scroll. Letting the menu shrink and scroll keeps the footer on
     screen at any height. */
  #sidebar_content,
  #sidebar_scrollable { flex-shrink: 1; min-height: 0; }
  #sidebar_scrollable { overflow-y: auto; }
}

/* ── Seats, hire and bookings pages ─────────────────────────────────────── */
/* Also used, and until now undefined, on the older forms: an invalid field's
   red border (places, services, pickups, the location picker) and the
   dashboard's card spacing. */
.border-destructive { border-color: var(--destructive); }
.mb-6               { margin-bottom: calc(var(--spacing) * 6); }

/* Booking page: the names on the seats, and buttons that keep their own width
   in a column (move, report lost). */
.list-decimal { list-style-type: decimal; }
.self-start   { align-self: flex-start; }

/* -- Footer links, language switch and paging (portal pages, 2026-10) ----- */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0; }
.gap-y-1 { row-gap: calc(var(--spacing) * 1); }
.pointer-events-none { pointer-events: none; }

/* -- The price on the estimate and hire pages (2026-10-03) -------------------
   The answer the page was asked for, at the top of the form: outlined in the
   primary colour, the total large. A journey edited after the price was
   given strikes the total through (data-stale, partials/hire-form-script). */
.quote-result {
  border: 2px solid var(--primary);
  box-shadow: 0 0 0 4px color-mix(in oklab, var(--primary) 16%, transparent);
  scroll-margin-top: 5rem;
  animation: quote-arrive 1.2s ease-out;
}
.quote-result-header { background: color-mix(in oklab, var(--primary) 7%, var(--card)); }
/* The heading takes focus from the page (tabindex=-1, never from Tab), so a
   ring there only ever marks the page's own move; the card is outlined. */
.quote-result [data-quote-title]:focus { outline: none; }
.quote-total {
  font-size: clamp(1.75rem, 1.25rem + 2.5vw, 2.5rem);
  font-weight: 700;
  line-height: 1.1;
  color: var(--foreground);
  font-variant-numeric: tabular-nums;
}
.quote-result[data-stale="1"] { border-color: var(--border); box-shadow: none; }
.quote-result[data-stale="1"] .quote-total { text-decoration: line-through; opacity: 0.6; }
@keyframes quote-arrive {
  from { box-shadow: 0 0 0 14px color-mix(in oklab, var(--primary) 32%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
  .quote-result { animation: none; }
}

/* ── Checkboxes you can see (user, 2026-10-04) ──────────────────────────── */
/* The bundle draws an unticked box's edge with --input, which measured about
   1.3:1 against the card in both themes (#e4e4e7 on white, #27272a on
   #09090b): all but invisible. A control's edge needs 3:1. zinc-500 is about
   4.9:1 on white and 4.1:1 on the dark card, and the edge is drawn at 2px.
   A ticked box keeps the bundle's blue fill; a refused one keeps its red. */
.kt-checkbox { border-width: 2px; }
.kt-checkbox:not(:checked):not(:indeterminate):not([aria-invalid="true"]) {
  border-color: var(--color-zinc-500);
}

/* ── The journey forms' price button (user, 2026-10-04) ─────────────────── */
/* "Get a price" / "Get an estimate" (partials/hire-fields). It was the
   bundle's 34px button with 13px text, in blue-500 in light mode: white text
   at about 3.7:1, under the 4.5:1 normal text needs. Now a 44px touch target
   with larger, bolder words, in blue-600 in both themes: white text about
   5.3:1, and the button about 5.3:1 against the light card and 3.8:1 against
   the dark one. Full width on a phone, where it was a small block at the end
   of a wrapped footer. */
.journey-price-btn {
  height: 2.75rem;
  padding-inline: 1.25rem;
  font-size: 0.9375rem;
  font-weight: 600;
}
.journey-price-btn i { font-size: 1.125rem; }
.journey-price-btn.kt-btn-primary {
  background-color: var(--color-blue-600);
  color: #fff;
}
@media (hover: hover) {
  .journey-price-btn.kt-btn-primary:hover { background-color: var(--color-blue-700); }
}
/* With a price showing it steps back to an outline - the price card's own
   button is the next step then - but its edge is drawn as firmly as a
   checkbox's. */
.journey-price-btn.kt-btn-outline { border-color: var(--color-zinc-500); }
@media (width < 40rem) {
  .journey-price-btn { width: 100%; justify-content: center; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Phones and tablets: targets, fields and the bottom bar (user, 2026-10-06)
   ───────────────────────────────────────────────────────────────────────────
   "Improve the visibility and usability of buttons and features in the
   client app. Key is to enhance the mobile experience." Measured at 375x667
   before: every button was the bundle's 34px (the small ones 28px), every
   field 34px tall with 13px text - iOS zooms the whole page into a field
   whose text is under 16px - a checkbox row 20px, the footer links 20px, the
   map's zoom buttons 30px and the drawer's links 38px; the dashboard's three
   actions made it 424px wide; and every page was two taps away, through the
   menu button in the top corner. Below lg only, where the sidebar is a
   drawer: the desktop keeps the bundle's sizes.
   ═══════════════════════════════════════════════════════════════════════════ */

/* A map paints its panes at z-index 400 and its controls at 1000. With no
   stacking context of its own it was drawn over the sticky header when
   scrolled under it, and would be over the bottom bar. */
.leaflet-container { isolation: isolate; }

.bottom-nav { display: none; }

@media (width < 64rem) {
  /* 44px: WCAG 2.5.5 / Apple HIG. min-height beats the bundle's height. */
  .kt-btn { min-height: 2.75rem; }
  .kt-btn.kt-btn-icon { min-width: 2.75rem; }

  .kt-input, .kt-select, .kt-textarea { font-size: 1rem; }
  .kt-input, .kt-select { min-height: 2.75rem; }

  /* The whole row is the target of a ticked box ("Round trip"), not the
     20px box. */
  label:has(> .kt-checkbox) { min-height: 2.75rem; }

  #footer-links a { display: inline-flex; align-items: center; min-height: 2.75rem; padding-inline: 0.5rem; }
  /* An address's search button is an icon in a plain .kt-btn (42px wide),
     and the logos that go home are links around a 29-36px image. */
  [data-location-picker] button[id$="-search"] { min-width: 2.75rem; }
  #sidebar_mobile_header a, #public-header a:has(> img) { min-height: 2.75rem; }
  #user-menu a, #user-menu button { min-height: 2.75rem; }
  #sidebar_menu .kt-menu-link { min-height: 2.75rem; }

  /* Leaflet's stylesheet loads after this one, with the partial that draws
     the map, so the container's class is added to win. */
  .leaflet-container.leaflet-touch .leaflet-bar a {
    width: 2.75rem; height: 2.75rem; line-height: 2.75rem; font-size: 1.375rem;
  }

  /* An empty table that is wider than the phone by design (it scrolls in its
     card) centred its "nothing yet" button 11px past the right edge. The
     message keeps to the part of the card on the screen. main is px-4 and
     the card has a 1px edge; the cell is px-4 too. */
  .table-empty-pinned { position: sticky; left: 0; width: calc(100vw - 4rem - 2px); }

  /* The bottom bar: the four places a client goes most, and Menu for the
     rest (components/bottom-nav). Labels are 12px in zinc-600 / zinc-400:
     the theme's muted zinc-500 is about 4.1:1 on the dark background. The
     page's own place is blue-600 / blue-400 (5.2:1 / 7.5:1) with a bar over
     it, so it does not rest on colour alone. */
  .bottom-nav {
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    position: fixed;
    inset-inline: 0;
    bottom: 0;
    z-index: 15; /* over the sticky header's 10, under the drawer's backdrop (19) */
    background-color: var(--background);
    border-top: 1px solid var(--border);
    box-shadow: 0 -2px 8px rgb(0 0 0 / 0.06);
    padding-bottom: env(safe-area-inset-bottom);
    padding-inline: env(safe-area-inset-left) env(safe-area-inset-right);
  }
  .bottom-nav-item {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.125rem;
    min-width: 0;
    min-height: 3.5rem;
    padding: 0.375rem 0.125rem;
    font-size: 0.75rem;
    font-weight: 500;
    line-height: 1.2;
    color: oklch(44.2% 0.017 285.786);
  }
  .dark .bottom-nav-item { color: oklch(70.5% 0.015 286.067); }
  .bottom-nav-item i { font-size: 1.375rem; }
  .bottom-nav-item span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .bottom-nav-item[aria-current="page"] { color: var(--color-blue-600); font-weight: 600; }
  .dark .bottom-nav-item[aria-current="page"] { color: oklch(70.7% 0.165 254.624); }
  .bottom-nav-item[aria-current="page"]::before {
    content: "";
    position: absolute;
    top: 0;
    inset-inline: 25%;
    height: 3px;
    border-radius: 0 0 3px 3px;
    background-color: currentColor;
  }
  .bottom-nav-item:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }

  /* Room under the footer, so the last of the page scrolls clear of it, and
     toasts (bottom right) shown above it rather than over it. The layout
     asks for the whole screen (viewport-fit=cover, so the safe-area insets
     are known); the sides are given back here for a phone held sideways,
     whose notch would otherwise sit over the page. */
  body.has-bottom-nav { padding-inline: env(safe-area-inset-left) env(safe-area-inset-right); }
  /* The body was h-full: held at the screen's height, with a long page
     overflowing it. The wrapper is stretched to that height, and a sticky
     header only sticks inside its parent, so one screen down (scrollY 607 on
     a 667px phone) the header - menu, theme, account - scrolled away. Grown
     with its content instead, the body still fills a short page. */
  body.has-bottom-nav { height: auto; min-height: 100%; }
  /* On the footer, the last thing on the page: the wrapper is stretched to
     the body's 100% height and a long page overflows it, so padding on the
     wrapper sat behind the content, not after it (the footer's last link
     ended 41px under the bar). 1rem is the footer's own py-4. */
  body.has-bottom-nav .kt-wrapper > footer { padding-bottom: calc(1rem + 3.5rem + 1px + env(safe-area-inset-bottom)); }
  body.has-bottom-nav.swal2-toast-shown .swal2-container.swal2-bottom-end {
    bottom: calc(3.5rem + 1px + env(safe-area-inset-bottom));
  }

  /* With the keyboard up a phone has half its height, and a fixed bar rides
     up on top of the keyboard, over the field being typed into. */
  body:has(:is(input:not([type="checkbox"], [type="radio"], [type="button"], [type="submit"], [type="hidden"]), textarea):focus) .bottom-nav {
    display: none;
  }
}

@media (width < 40rem) {
  /* Book a Seat / Hire a Vehicle / Book a Service at the top of the
     dashboard and My Bookings: three tiles side by side, icon over words,
     instead of a row of buttons wider than the phone. */
  .quick-actions { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); width: 100%; }
  .quick-actions > .kt-btn {
    flex-direction: column; height: auto; min-height: 4.5rem; gap: 0.375rem;
    padding: 0.625rem 0.25rem; white-space: normal; text-align: center; line-height: 1.2;
  }
  .quick-actions > .kt-btn i { margin: 0; font-size: 1.375rem; }
}
