/*
  The web app's design system.

  Deliberately hand-written and framework-free: the phone app already fixed the
  spacing scale, the corner radii and the brand colour, and a CSS framework would
  bring its own set of all three. The spacing and radius tokens below are the
  same ones as turaveller_mobile/lib/core/ui/tokens.dart, so a screen built here
  and a screen built there measure the same.

  The colours go further than the phone's Material seed. This is read by someone
  who is looking forward to something, and a grid of white cards on grey does not
  sound like that.

  The model is a travel postcard, not a dashboard: warm cream ground, a family of
  pastels doing the large areas, and the deep teal kept for the small ones - a
  button, a heading, an icon. Roughly 70% neutral, 20% pastel, 10% saturated. The
  rule that keeps it from turning into wallpaper is that a pastel always means
  something: on the home screen each one is a stage of a trip, from mint while it
  is still an idea to lavender once it is a memory.
*/

:root {
    /*
      Light unless the reader says otherwise. A trip app opening in a dark
      window was the single loudest complaint about the last look, and the OS
      preference is a poor proxy for it: someone who keeps their editor dark
      still wants a postcard to arrive on white paper. Night stays one pick away
      in the top bar, and "system" is there for whoever wants to hand the choice
      back to the OS.
    */
    color-scheme: light;

    /* Spacing, matching AppSpacing in the phone app. */
    --space-xs: 4px;
    --space-sm: 8px;
    --space-md: 12px;
    --space-lg: 16px;
    --space-xl: 20px;
    --space-xxl: 28px;
    --space-huge: 40px;

    /* Corners, matching AppRadius. */
    --radius-sm: 12px;
    --radius-md: 20px;
    --radius-lg: 22px;
    --radius-pill: 999px;

    /*
      One palette, written once. `light-dark()` picks the arm from the root's
      `color-scheme` above, so the same declaration serves both appearances
      without a second copy of the list.

      The dark arm is dusk, not night, and it is not a pastel scheme with the
      lights off: a pastel is a tint of white, so on a dark ground it has to be
      re-made as a deep muted version of the same hue with a light ink on top.
      Both arms carry the same five hues in the same order, which is what lets
      the status colours below mean the same thing in either.

      Its shadows are deep navy rather than a darkened version of the day's
      warm ground. A dark green-grey reads as mould rather than as evening, and
      it dulls every pastel laid over it, since each one then sits on a ground
      that shares its own hue. Navy shares none of them, so the five stay
      distinguishable — and the pastels themselves are pitched a step brighter
      and a step more saturated than a strict darkening would give, which is
      what separates a chip from the card it sits on without a border.
    */

    /* The brand seed, #176B5B, warmed towards the deep teal the pastels sit
       under. Small areas only: a button, a heading, an icon. */
    --brand: light-dark(#256b63, #6fd6c4);
    --brand-strong: light-dark(#17504a, #97e7da);
    --brand-bright: light-dark(#37907f, #47b8a4);
    --brand-soft: light-dark(#cfefe4, #24544c);
    --brand-on: light-dark(#ffffff, #06322c);
    --accent: light-dark(#ffe9a8, #f0c964);
    --accent-on: light-dark(#5c4300, #3a2a00);

    /*
      The postcard's five pastels. `-ink` is the text that sits on the pastel;
      it is a darkened version of the same hue rather than the body colour, so a
      chip reads as one object instead of black type on a coloured patch.
    */
    --mint: light-dark(#cfefe4, #38685f);
    --mint-ink: light-dark(#1a5b50, #b6ece0);
    --sky: light-dark(#d9ecff, #355f7c);
    --sky-ink: light-dark(#1b5580, #b8ddf7);
    --peach: light-dark(#ffd6c7, #754b43);
    --peach-ink: light-dark(#95452c, #ffcdba);
    --butter: light-dark(#ffe9a8, #69592f);
    --butter-ink: light-dark(#7a5504, #f7e0a0);
    --lavender: light-dark(#e7ddf8, #594d73);
    --lavender-ink: light-dark(#57438a, #dbcef3);

    /* Kept under their old names so the notice, chip and map styles below do
       not all have to be renamed to say the same thing. */
    --sun: light-dark(#ffb45c, #f0a94e);
    --sun-soft: var(--butter);
    --sea: light-dark(#6fb3e0, #7cc4e8);
    --sea-soft: var(--sky);

    /*
      The gradients are built from stops rather than written per theme, because
      a whole `linear-gradient(...)` is not a colour and `light-dark()` only
      takes colours. Dark runs the brand ramp the other way: the light arm is
      darkest at the start so white sits on it, the dark arm is lightest there
      so the near-black `--brand-on` does.
    */
    --grad-from: light-dark(#17504a, #86e3d3);
    --grad-to: light-dark(#37907f, #47b8a4);
    --grad-brand: linear-gradient(135deg, var(--grad-from), var(--grad-to));
    --grad-sunset: linear-gradient(90deg, var(--mint-ink), var(--sun) 55%, var(--peach-ink));

    /* Cream by day, deep navy by night: the ground the whole palette is mixed
       for. The three dark steps are far enough apart to separate a raised card
       from the page without a shadow doing the work. */
    --surface: light-dark(#fffdfa, #282d38);
    --surface-sunken: light-dark(#fff9f3, #20232c);
    --surface-raised: light-dark(#ffffff, #313744);
    --border: light-dark(#f0e4d8, #485264);
    --border-strong: light-dark(#d8c8b8, #5c667a);

    /*
      The two light sources behind the top of the page. Lavender on one shoulder
      and mint on the other, because one even wash reads as a gradient someone
      forgot to finish, and the pair is what makes a night sky rather than a
      grey one.
    */
    --wash: light-dark(rgb(255 214 199 / 40%), rgb(78 183 165 / 17%));
    --wash-lift: light-dark(rgb(231 221 248 / 55%), rgb(116 94 170 / 24%));

    /* Ivory rather than white: the day's type is warm, and a cold grey on navy
       would read as a different family. */
    --text: light-dark(#28332f, #f7f2ed);
    --text-muted: light-dark(#63706a, #bbb8c2);
    --text-faint: light-dark(#8a938d, #9195a3);
    --danger: light-dark(#b3261e, #ff897d);
    /* Held darker than the other four. A danger notice fills its block with
       this and writes on it in --danger, so the pastel and its ink are the same
       hue: lifting it as far as the rest would have taken that pair under 4.5:1. */
    --danger-soft: light-dark(#ffe3de, #4e2a26);
    --warning: var(--butter-ink);
    --warning-soft: var(--butter);
    --success: var(--mint-ink);
    --success-soft: var(--mint);
    /*
      Lighter at night than by day, which is the opposite of the instinct. A
      card on a dark ground is already told apart by its own lightness and its
      border; piling black on top of near-black only muddies the space between
      them, and the page reads as smudged rather than as lifted.
    */
    --shadow: 0 1px 2px light-dark(rgb(90 60 40 / 5%), rgb(0 0 0 / 20%)),
        0 8px 24px light-dark(rgb(90 60 40 / 7%), rgb(0 0 0 / 14%));
    --shadow-lifted: 0 2px 6px light-dark(rgb(90 60 40 / 7%), rgb(0 0 0 / 24%)),
        0 18px 40px light-dark(rgb(90 60 40 / 11%), rgb(0 0 0 / 18%));

    /* The tone a .tone--* class overrides. Named here so anything that reads
       --tone outside a toned card still resolves to a real colour. */
    --tone: var(--mint);
    --tone-ink: var(--mint-ink);

    --font: "Pretendard", -apple-system, BlinkMacSystemFont, "Segoe UI",
        "Apple SD Gothic Neo", "Malgun Gothic", "Noto Sans KR", sans-serif;
}

/*
  The appearance picker writes data-theme; these three rules are the whole of
  what it does. Pinning `color-scheme` moves the palette above and the browser's
  own widgets - scrollbars, date pickers, the language select - in one step.
*/
:root[data-theme="system"] {
    color-scheme: light dark;
}

:root[data-theme="light"] {
    color-scheme: light;
}

:root[data-theme="dark"] {
    color-scheme: dark;
}

* {
    box-sizing: border-box;
}

/*
  A component class that sets `display` outranks the `hidden` attribute, which
  is how the sign-in page showed its "sign-in failed" line to everybody who had
  not tried to sign in yet. Hidden means hidden, whatever else an element is.
*/
[hidden] {
    display: none !important;
}

html,
body {
    margin: 0;
    padding: 0;
}

body {
    /* Two lights behind the top of the page, so the shell has a source instead
       of an even fill, and a horizon rather than a spotlight. Fixed, so they do
       not slide away as the page scrolls. */
    background:
        radial-gradient(90% 55% at 90% -10%, var(--wash-lift), transparent 65%),
        radial-gradient(100% 65% at 5% 0%, var(--wash), transparent 68%),
        var(--surface-sunken);
    background-attachment: fixed;
    color: var(--text);
    font-family: var(--font);
    font-size: 15px;
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3 {
    margin: 0;
    line-height: 1.25;
    letter-spacing: -0.01em;
}

h1 {
    font-size: clamp(1.65rem, 1.2rem + 1.6vw, 2.05rem);
    font-weight: 800;
    letter-spacing: -0.022em;
}

h2 {
    font-size: 1.18rem;
    font-weight: 750;
}

h3 {
    font-size: 1rem;
}

p {
    margin: 0;
}

a {
    color: var(--brand);
    text-decoration: none;
}

a:hover {
    text-decoration: underline;
}

:focus-visible {
    outline: 2px solid var(--brand);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

/*
  Routing moves focus to the page heading so a screen reader announces the new
  page. That focus is the app's doing rather than the reader's, and ringing a
  whole 1.6rem heading in brand green on every navigation reads as an error.
  The heading is not tabbable, so nothing is lost by leaving it unpainted.
*/
h1:focus,
h1:focus-visible {
    outline: none;
}

/* ---------- Shell ---------- */

.shell {
    display: flex;
    min-height: 100vh;
    flex-direction: column;
}

.topbar {
    position: sticky;
    top: 0;
    z-index: 20;
    display: flex;
    align-items: center;
    gap: var(--space-lg);
    padding: var(--space-md) var(--space-xl);
    background: var(--surface);
    border-bottom: 1px solid var(--border);
}

/*
  The bar sits over the content, so it lets a little of it through. The opaque
  rule above is the fallback: a solid bar beats an unreadable one wherever
  backdrop-filter is missing.
*/
@supports (backdrop-filter: blur(4px)) or (-webkit-backdrop-filter: blur(4px)) {
    .topbar {
        background: color-mix(in srgb, var(--surface) 78%, transparent);
        -webkit-backdrop-filter: saturate(180%) blur(14px);
        backdrop-filter: saturate(180%) blur(14px);
    }
}

.topbar__brand {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    font-weight: 700;
    color: var(--text);
    font-size: 1.05rem;
}

.topbar__brand:hover {
    text-decoration: none;
}

.brand-wordmark {
    display: inline-flex;
    align-items: baseline;
    letter-spacing: -0.025em;
}

.brand-wordmark__tu {
    color: var(--brand-strong);
    font-weight: 800;
}

.brand-wordmark--signin {
    margin: 0 auto;
    color: var(--text-muted);
    font-size: 0.95rem;
    font-weight: 700;
}

.brand-loader {
    --brand-loader-size: 80px;
    position: relative;
    display: inline-block;
    width: var(--brand-loader-size);
    height: var(--brand-loader-size);
    flex: none;
}

.brand-loader__layer {
    position: absolute;
    inset: 0;
    background: #176b5b;
    -webkit-mask: url("/images/turaveller-mark.png") center / contain no-repeat;
    mask: url("/images/turaveller-mark.png") center / contain no-repeat;
}

.brand-loader__fill {
    background: #f26f5b;
    clip-path: circle(7% at 72% 30%);
    animation: brand-loader-fill 1.8s ease-in-out infinite;
    will-change: clip-path;
}

@keyframes brand-loader-fill {
    0%,
    10%,
    100% {
        clip-path: circle(7% at 72% 30%);
    }

    56%,
    68% {
        clip-path: circle(112% at 72% 30%);
    }
}

.route-loading {
    display: grid;
    min-height: min(420px, 70vh);
    place-items: center;
}

.topbar__mark {
    display: block;
    width: 28px;
    height: 28px;
    border-radius: 9px;
    object-fit: cover;
    box-shadow: 0 2px 8px color-mix(in srgb, var(--brand) 45%, transparent);
}

.topbar__nav {
    display: flex;
    align-items: center;
    gap: var(--space-md);
    margin-inline-start: auto;
    flex-wrap: wrap;
}

.topbar__primary,
.topbar__utilities {
    display: flex;
    align-items: center;
    gap: var(--space-xs);
}

.topbar__language {
    width: auto;
    padding: 5px 10px;
    font-size: 0.85rem;
}

.topbar__link {
    position: relative;
    padding: 6px 12px;
    border-radius: var(--radius-pill);
    color: var(--text-muted);
    font-weight: 600;
    font-size: 0.9rem;
}

.topbar__link:hover {
    background: var(--surface-sunken);
    text-decoration: none;
}

.topbar__link.active {
    background: var(--brand-soft);
    color: var(--brand-strong);
}

@media (max-width: 640px) {
    .topbar {
        padding: 10px var(--space-lg);
    }

    .topbar__brand > span:last-child,
    .topbar__user {
        display: none;
    }

    .topbar__nav {
        gap: var(--space-xs);
        flex-wrap: nowrap;
    }

    .topbar__primary {
        position: fixed;
        inset-inline: 0;
        inset-block-end: 0;
        z-index: 30;
        justify-content: stretch;
        padding: 7px max(var(--space-md), env(safe-area-inset-right))
            calc(7px + env(safe-area-inset-bottom))
            max(var(--space-md), env(safe-area-inset-left));
        border-top: 1px solid var(--border);
        background: var(--surface);
        box-shadow: 0 -4px 18px rgb(20 32 29 / 7%);
    }

    .topbar__primary .topbar__link {
        display: flex;
        min-height: 42px;
        flex: 1;
        align-items: center;
        justify-content: center;
        text-align: center;
    }

    .topbar__language {
        max-width: 74px;
    }

    /* The primary links sit on the bottom edge, so the page ends above them. */
    .page {
        padding: var(--space-lg) var(--space-lg) calc(var(--space-huge) + 64px);
    }
}

.page {
    width: 100%;
    max-width: 1120px;
    margin: 0 auto;
    padding: var(--space-xl) var(--space-xl) var(--space-huge);
}

.page__header {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--space-lg);
    flex-wrap: wrap;
    margin-bottom: var(--space-xl);
}

.page__subtitle {
    color: var(--text-muted);
    margin-top: var(--space-xs);
}

/* ---------- Surfaces ---------- */

.card {
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: var(--space-lg);
    box-shadow: var(--shadow);
}

/* A short rule of the brand gradient above the page name: one stroke of colour
   on every screen, including the ones that are otherwise all text. */
.page__header h1 {
    padding-top: var(--space-sm);
    background: var(--grad-sunset) no-repeat top left / 42px 4px;
    border-radius: 2px;
}

.card--flat {
    box-shadow: none;
}

.card__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-md);
    margin-bottom: var(--space-md);
}

.stack {
    display: flex;
    flex-direction: column;
    gap: var(--space-md);
}

.stack--tight {
    gap: var(--space-sm);
}

.stack--loose {
    gap: var(--space-xl);
}

.row {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    flex-wrap: wrap;
}

.row--between {
    justify-content: space-between;
}

.grid {
    display: grid;
    gap: var(--space-lg);
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
}

.split {
    display: grid;
    gap: var(--space-xl);
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
    align-items: start;
}

@media (max-width: 900px) {
    .split {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* ---------- Text ---------- */

.muted {
    color: var(--text-muted);
}

.faint {
    color: var(--text-faint);
    font-size: 0.85rem;
}

.small {
    font-size: 0.85rem;
}

.strong {
    font-weight: 650;
}

.mono {
    font-family: ui-monospace, "Cascadia Mono", "SFMono-Regular", monospace;
    letter-spacing: 0.08em;
}

/* A stop being dragged, and the one it would land on. Both are drawn rather
   than only the cursor changing: dropping onto the wrong row is the mistake
   this feedback exists to prevent. */
.stop[draggable="true"] {
    cursor: grab;
}

.stop--dragging {
    opacity: 0.45;
}

.stop--drop-target {
    outline: 2px dashed var(--brand);
    outline-offset: -2px;
}

/* ---------- Map ---------- */

.map {
    height: 520px;
    border-radius: var(--radius-md);
    overflow: hidden;
    /* Leaflet paints its own tiles; without a ground of our own the panel
       flashes the page background while they load. */
    background: var(--surface-sunken);
}

/* A map inside a form, where it is one field among several rather than the
   thing being read. */
.map--compact {
    height: 260px;
}

/* A numbered stop, because the order is the point of an itinerary. */
.map-pin span {
    display: grid;
    place-items: center;
    width: 26px;
    height: 26px;
    border-radius: var(--radius-pill);
    background: var(--grad-brand);
    color: var(--brand-on);
    font-size: 0.8rem;
    font-weight: 700;
    box-shadow: var(--shadow);
}

/* ---------- Controls ---------- */

.button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-sm);
    padding: 9px 16px;
    border: 1px solid transparent;
    border-radius: var(--radius-pill);
    background: var(--grad-brand);
    color: var(--brand-on);
    font: inherit;
    font-weight: 650;
    font-size: 0.9rem;
    cursor: pointer;
    /* A label is one line: a button in a narrow column shrinks the row it is
       in, not the word it carries. */
    white-space: nowrap;
    flex-shrink: 0;
    box-shadow: 0 1px 2px color-mix(in srgb, var(--brand-strong) 22%, transparent);
    transition: filter 120ms ease, background 120ms ease,
        transform 120ms ease, box-shadow 120ms ease;
}

.button:hover:not(:disabled) {
    filter: brightness(1.07);
    transform: translateY(-1px);
    box-shadow: 0 4px 14px color-mix(in srgb, var(--brand) 34%, transparent);
    text-decoration: none;
}

/* The press has to land somewhere, or it stays a picture of a button. */
.button:active:not(:disabled) {
    transform: translateY(0) scale(0.98);
    box-shadow: none;
}

.button:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

.button--ghost {
    background: transparent;
    color: var(--text);
    border-color: var(--border-strong);
    box-shadow: none;
}

.button--ghost:hover:not(:disabled) {
    background: var(--surface-sunken);
    border-color: var(--brand);
    color: var(--brand-strong);
    box-shadow: none;
    filter: none;
}

.button--quiet {
    background: transparent;
    color: var(--text-muted);
    border-color: transparent;
    padding: 6px 10px;
    box-shadow: none;
}

.button--quiet:hover:not(:disabled) {
    background: var(--surface-sunken);
    box-shadow: none;
    filter: none;
}

.button--danger {
    background: transparent;
    color: var(--danger);
    border-color: var(--border-strong);
    box-shadow: none;
}

.button--small {
    padding: 5px 11px;
    font-size: 0.82rem;
}

.button--icon {
    padding: 5px 9px;
    min-width: 32px;
}

/* A finger is not a mouse pointer: 32px is under every touch-target guideline. */
@media (pointer: coarse) {
    .button--icon {
        min-width: 44px;
        min-height: 44px;
    }
}

.field {
    display: flex;
    flex-direction: column;
    gap: var(--space-xs);
}

.field__label {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-muted);
}

.input,
.select,
.textarea {
    width: 100%;
    padding: 9px 12px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text);
    font: inherit;
    font-size: 0.92rem;
}

.textarea {
    min-height: 84px;
    resize: vertical;
}

.input:focus,
.select:focus,
.textarea:focus {
    outline: none;
    border-color: var(--brand);
    box-shadow: 0 0 0 3px var(--brand-soft);
}

.checkbox {
    display: flex;
    align-items: flex-start;
    gap: var(--space-sm);
    cursor: pointer;
}

.checkbox input {
    margin-top: 3px;
    accent-color: var(--brand);
}

.form-grid {
    display: grid;
    gap: var(--space-md);
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

/* ---------- Chips, badges, tabs ---------- */

.chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-xs);
    padding: 3px 10px;
    border-radius: var(--radius-pill);
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    color: var(--text-muted);
    font-size: 0.8rem;
    font-weight: 600;
    white-space: nowrap;
}

.chip--brand {
    background: var(--brand-soft);
    border-color: transparent;
    color: var(--brand-strong);
}

.chip--warning {
    background: var(--warning-soft);
    border-color: transparent;
    color: var(--warning);
}

.chip--danger {
    background: var(--danger-soft);
    border-color: transparent;
    color: var(--danger);
}

.chip--success {
    background: var(--success-soft);
    border-color: transparent;
    color: var(--success);
}

/* Wears whatever tone its card is in. */
.chip--tone {
    background: color-mix(in srgb, var(--tone) 70%, #fff);
    border-color: color-mix(in srgb, var(--tone-ink) 22%, transparent);
    color: var(--tone-ink);
    font-weight: 700;
}

.chip--accent {
    background: linear-gradient(120deg, var(--accent), var(--sun));
    border-color: transparent;
    color: var(--accent-on);
    font-weight: 700;
}

.badge {
    display: inline-grid;
    place-items: center;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: var(--radius-pill);
    background: var(--danger);
    color: #fff;
    font-size: 0.7rem;
    font-weight: 700;
}

.tabs {
    display: flex;
    gap: var(--space-xs);
    border-bottom: 1px solid var(--border);
    margin-bottom: var(--space-lg);
    overflow-x: auto;
}

.tab {
    padding: 8px 14px;
    border: none;
    border-bottom: 2px solid transparent;
    background: none;
    color: var(--text-muted);
    font: inherit;
    font-weight: 600;
    font-size: 0.92rem;
    cursor: pointer;
    white-space: nowrap;
}

.tab.active {
    color: var(--brand-strong);
    border-bottom-color: transparent;
    background: var(--grad-sunset) no-repeat bottom left / 100% 3px;
}

/* ---------- Lists ---------- */

.list {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--surface-raised);
    overflow: hidden;
}

.list__item {
    display: flex;
    align-items: flex-start;
    gap: var(--space-md);
    padding: var(--space-md) var(--space-lg);
    border-bottom: 1px solid var(--border);
}

.list__item:last-child {
    border-bottom: none;
}

.list__item--unread {
    background: var(--brand-soft);
}

.list__body {
    flex: 1;
    min-width: 0;
}

.avatar {
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    flex: none;
    border-radius: var(--radius-pill);
    background: linear-gradient(140deg, var(--brand-soft), var(--sun-soft));
    color: var(--brand-strong);
    font-weight: 700;
    font-size: 0.8rem;
    object-fit: cover;
    overflow: hidden;
}

/* ---------- Trip cards ---------- */

/*
  A trip's stage, as a colour: mint while it is an idea, butter once it is close
  enough to pack for, peach while it is happening, lavender once it is a memory.
  The class sets two custom properties rather than painting anything itself, so
  one card can hand the same pair to its poster, its border and its chip and
  they all agree without any of them naming a hue.
*/
.tone--mint {
    --tone: var(--mint);
    --tone-ink: var(--mint-ink);
}

.tone--butter {
    --tone: var(--butter);
    --tone-ink: var(--butter-ink);
}

.tone--peach {
    --tone: var(--peach);
    --tone-ink: var(--peach-ink);
}

.tone--lavender {
    --tone: var(--lavender);
    --tone-ink: var(--lavender-ink);
}

.tone--sky {
    --tone: var(--sky);
    --tone-ink: var(--sky-ink);
}


/*
  A trip is a place before it is a row in a list, so a card is drawn as a small
  poster: a band of sky with a sun and a hill over a plain block of type. The
  band's colour is the trip's stage - see the .tone--* classes above - and the
  sun and hill move per trip, so twelve cards in a grid are twelve pictures
  rather than one repeated twelve times.
*/
.trip-card {
    position: relative;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--surface-raised);
    color: inherit;
    box-shadow: var(--shadow);
    transition: transform 140ms ease, border-color 140ms ease,
        box-shadow 140ms ease;
}

.trip-card__poster {
    position: relative;
    display: block;
    height: 92px;
    flex: none;
    background:
        /* The near hill, then the far one, then the sun behind both. */
        radial-gradient(150px 60px at calc(var(--hill-x, 20%) + 34%) 145%,
            color-mix(in srgb, var(--tone-ink) 32%, var(--tone)) 0 99%, transparent 100%),
        radial-gradient(190px 66px at var(--hill-x, 20%) 130%,
            color-mix(in srgb, var(--tone-ink) 15%, var(--tone)) 0 99%, transparent 100%),
        radial-gradient(circle 26px at var(--sun-x, 78%) 34%,
            var(--sun) 0 46%,
            color-mix(in srgb, var(--sun) 30%, transparent) 48%, transparent 76%),
        linear-gradient(180deg,
            color-mix(in srgb, var(--tone) 26%, #fff) 0%,
            color-mix(in srgb, var(--tone) 82%, #fff) 100%);
}

/* Five compositions, picked from the trip's own id so a given trip is always
   drawn the same way. Nothing here means anything - it is only variety. */
.trip-card[data-art="1"] .trip-card__poster {
    --sun-x: 26%;
    --hill-x: 76%;
}

.trip-card[data-art="2"] .trip-card__poster {
    --sun-x: 62%;
    --hill-x: 8%;
}

.trip-card[data-art="3"] .trip-card__poster {
    --sun-x: 88%;
    --hill-x: 44%;
}

.trip-card[data-art="4"] .trip-card__poster {
    --sun-x: 42%;
    --hill-x: 92%;
}

.trip-card__body {
    display: flex;
    flex: 1;
    flex-direction: column;
    gap: var(--space-sm);
    padding: var(--space-lg);
}

.trip-card:hover {
    transform: translateY(-3px);
    border-color: color-mix(in srgb, var(--tone-ink) 45%, var(--border));
    box-shadow: var(--shadow-lifted);
    text-decoration: none;
}

.trip-card__title {
    font-size: 1.08rem;
    font-weight: 750;
    letter-spacing: -0.012em;
}

/* A finished trip keeps its lavender, at the volume of a memory. */
.trip-card--past .trip-card__poster {
    opacity: 0.72;
}

.home-action-panel {
    margin-bottom: var(--space-xl);
}

/*
  The next trip, drawn as the poster you would pin up for it: sky at the top,
  a sun, two hills, and the type sitting on the light part. The old version of
  this was a near-black green block, which is what made the whole screen read
  as an admin panel - the biggest object on the home screen sets the tone for
  everything under it.
*/
.home-featured {
    position: relative;
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--space-xl);
    overflow: hidden;
    min-height: 230px;
    padding: var(--space-xxl);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background:
        radial-gradient(560px 118px at 74% 140%,
            color-mix(in srgb, var(--tone-ink) 34%, var(--tone)) 0 99%, transparent 100%),
        radial-gradient(680px 148px at 12% 134%,
            color-mix(in srgb, var(--tone-ink) 16%, var(--tone)) 0 99%, transparent 100%),
        radial-gradient(circle 64px at 84% 26%,
            var(--sun) 0 48%,
            color-mix(in srgb, var(--sun) 32%, transparent) 50%, transparent 78%),
        linear-gradient(180deg,
            color-mix(in srgb, var(--sky) 84%, #fff) 0%,
            color-mix(in srgb, var(--tone) 44%, #fff) 62%,
            color-mix(in srgb, var(--tone) 70%, #fff) 100%);
    color: var(--text);
    box-shadow: var(--shadow);
    transition: transform 160ms ease, box-shadow 160ms ease;
}

.home-featured:hover {
    text-decoration: none;
    transform: translateY(-2px);
    box-shadow: var(--shadow-lifted);
}

.home-featured__content {
    position: relative;
    z-index: 1;
    display: flex;
    min-width: 0;
    flex-direction: column;
    gap: var(--space-md);
}

.home-featured__destination {
    font-size: 0.82rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--tone-ink);
}

.home-featured__dates {
    color: var(--text-muted);
}

.home-featured__title {
    margin-top: var(--space-xs);
    font-size: clamp(1.6rem, 3.4vw, 2.5rem);
    font-weight: 800;
    letter-spacing: -0.028em;
    color: var(--brand-strong);
}

.home-featured__action {
    position: relative;
    z-index: 1;
    flex: none;
    padding: 10px 16px;
    border-radius: var(--radius-pill);
    background: var(--grad-brand);
    color: var(--brand-on);
    font-weight: 700;
    box-shadow: 0 4px 14px color-mix(in srgb, var(--brand) 34%, transparent);
}

.invite-entry {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: var(--space-sm);
}

.invite-entry .input {
    font-size: 1rem;
}

.invite-preview {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--space-md);
    padding: var(--space-lg);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--surface-sunken);
}

.invite-preview__mark {
    display: grid;
    place-items: center;
    width: 48px;
    height: 48px;
    border-radius: 16px;
    background: var(--brand-soft);
    color: var(--brand-strong);
    font-size: 1.25rem;
}

.invite-preview__body {
    min-width: 0;
}

.past-trips {
    padding-top: var(--space-sm);
    border-top: 1px solid var(--border);
}

.past-trips summary {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    padding: var(--space-md) 0;
    cursor: pointer;
    list-style-position: inside;
}

.past-trips__grid {
    padding-top: var(--space-sm);
}

@media (max-width: 640px) {
    .home-featured {
        align-items: stretch;
        flex-direction: column;
        padding: var(--space-xl);
    }

    .home-featured__action {
        align-self: flex-start;
    }

    .invite-entry {
        grid-template-columns: minmax(0, 1fr);
    }

    .invite-preview {
        grid-template-columns: auto minmax(0, 1fr);
    }

    .invite-preview > .button {
        grid-column: 1 / -1;
        width: 100%;
    }
}

/* ---------- Itinerary ---------- */

.day {
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--surface-raised);
    overflow: hidden;
}

.day__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-md);
    padding: var(--space-md) var(--space-lg);
    background: var(--surface-sunken);
    border-bottom: 1px solid var(--border);
    flex-wrap: wrap;
}

.stop {
    display: flex;
    gap: var(--space-md);
    padding: var(--space-md) var(--space-lg);
    border-bottom: 1px solid var(--border);
}

.stop:last-child {
    border-bottom: none;
}

.stop--done .stop__name {
    text-decoration: line-through;
    color: var(--text-faint);
}

.stop__time {
    flex: none;
    width: 58px;
    font-variant-numeric: tabular-nums;
    font-weight: 650;
    color: var(--brand-strong);
}

.stop__body {
    flex: 1;
    min-width: 0;
}

.stop__name {
    font-weight: 650;
}

.stop__time-day {
    margin-left: 2px;
    font-size: 0.7rem;
    font-weight: 650;
    vertical-align: super;
}

.stop__actions {
    display: flex;
    gap: var(--space-xs);
    flex: none;
    align-items: flex-start;
}

/*
  On a phone the five controls on the right take the whole width and squeeze the
  place name down to a few pixels, which renders it one character per line. The
  name keeps the first row to itself and the controls wrap onto a second, lined
  up under it past the time column.
*/
@media (max-width: 640px) {
    .stop {
        flex-wrap: wrap;
    }

    .stop__body {
        flex: 1 1 auto;
    }

    .stop__actions {
        flex: 1 0 100%;
        flex-wrap: wrap;
        padding-left: 70px;
    }
}

.leg {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    padding: var(--space-xs) var(--space-lg) var(--space-xs) 76px;
    color: var(--text-faint);
    font-size: 0.82rem;
}

/* ---------- States ---------- */

.notice {
    display: flex;
    align-items: flex-start;
    gap: var(--space-sm);
    padding: var(--space-md) var(--space-lg);
    border-radius: var(--radius-sm);
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    color: var(--text-muted);
    font-size: 0.9rem;
}

.notice--warning {
    background: var(--warning-soft);
    border-color: transparent;
    color: var(--warning);
}

.notice--danger {
    background: var(--danger-soft);
    border-color: transparent;
    color: var(--danger);
}

.notice--success {
    background: var(--success-soft);
    border-color: transparent;
    color: var(--success);
}

.empty {
    display: grid;
    place-items: center;
    gap: var(--space-md);
    padding: var(--space-huge) var(--space-xl);
    border: 1px dashed color-mix(in srgb, var(--brand) 40%, var(--border-strong));
    border-radius: var(--radius-lg);
    background: radial-gradient(90% 120% at 50% 0%,
        color-mix(in srgb, var(--brand-soft) 60%, transparent) 0%, transparent 70%);
    color: var(--text-muted);
    text-align: center;
}

.empty__mark {
    display: grid;
    place-items: center;
    width: 46px;
    height: 46px;
    border-radius: var(--radius-pill);
    background: var(--surface-raised);
    border: 1px solid var(--border);
    color: var(--brand);
    box-shadow: var(--shadow);
}

.skeleton {
    height: 14px;
    border-radius: var(--radius-sm);
    background: linear-gradient(
        90deg,
        var(--surface-sunken) 25%,
        var(--border) 37%,
        var(--surface-sunken) 63%
    );
    background-size: 400% 100%;
    animation: skeleton 1.4s ease infinite;
}

@keyframes skeleton {
    0% {
        background-position: 100% 50%;
    }

    100% {
        background-position: 0 50%;
    }
}

@media (prefers-reduced-motion: reduce) {
    .skeleton,
    .brand-loader__fill {
        animation: none;
    }

    .trip-card:hover,
    .home-featured:hover,
    .button:hover:not(:disabled),
    .button:active:not(:disabled) {
        transform: none;
    }
}

.live-dot {
    width: 8px;
    height: 8px;
    border-radius: var(--radius-pill);
    background: var(--success);
    flex: none;
}

.live-dot--off {
    background: var(--text-faint);
}

/* ---------- Toasts ---------- */

.toasts {
    position: fixed;
    inset-block-end: var(--space-xl);
    inset-inline-end: var(--space-xl);
    z-index: 60;
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
    max-width: min(360px, calc(100vw - 2 * var(--space-xl)));
}

.toast {
    display: flex;
    align-items: flex-start;
    gap: var(--space-sm);
    padding: var(--space-md) var(--space-lg);
    border-radius: var(--radius-sm);
    background: var(--surface-raised);
    border: 1px solid var(--border);
    box-shadow: var(--shadow);
    font-size: 0.9rem;
}

.toast--danger {
    border-color: var(--danger);
    color: var(--danger);
}

/* ---------- Sign-in ---------- */

/*
  The first screen anyone sees, so it is the one that has to say what the app is
  for. Same postcard the home screen is drawn as, at the scale of a window: sky,
  a sun, two hills, and the card standing on the near one.
*/
.signin {
    position: relative;
    display: grid;
    place-items: center;
    min-height: 100vh;
    overflow: hidden;
    padding: var(--space-xl);
    background:
        radial-gradient(circle 96px at 78% 20%,
            var(--sun) 0 44%,
            color-mix(in srgb, var(--sun) 32%, transparent) 46%, transparent 74%),
        linear-gradient(180deg,
            var(--sky) 0%,
            color-mix(in srgb, var(--sky) 46%, #fff) 34%,
            color-mix(in srgb, var(--peach) 52%, #fff) 68%,
            color-mix(in srgb, var(--peach) 74%, #fff) 100%);
}

/*
  The hills get their own layer rather than more stops on the gradient above:
  measured from the bottom edge they keep the same height on a laptop and on a
  phone, where a percentage of the viewport would stretch them into stripes.
*/
.signin::before {
    content: "";
    position: absolute;
    inset-inline: 0;
    inset-block-end: 0;
    height: 420px;
    background:
        /* Near, middle, far: three ridges, each lighter and higher than the one
           in front of it, which is the whole of the depth in this picture. */
        radial-gradient(58% 150px at 82% 102%,
            color-mix(in srgb, var(--peach-ink) 30%, var(--peach)) 0 99%, transparent 100%),
        radial-gradient(74% 236px at 16% 106%,
            color-mix(in srgb, var(--mint-ink) 24%, var(--mint)) 0 99%, transparent 100%),
        radial-gradient(120% 330px at 54% 112%,
            color-mix(in srgb, var(--sky-ink) 14%, var(--sky)) 0 99%, transparent 100%);
    pointer-events: none;
}

.signin__card {
    position: relative;
    z-index: 1;
    width: min(420px, 100%);
    display: flex;
    flex-direction: column;
    gap: var(--space-lg);
    padding: var(--space-xxl);
    border-radius: var(--radius-lg);
    background: var(--surface-raised);
    border: 1px solid var(--border);
    box-shadow: var(--shadow-lifted);
    text-align: center;
}

.signin__mark {
    display: block;
    width: 84px;
    height: 84px;
    margin: 0 auto;
    border-radius: 22px;
    object-fit: cover;
    box-shadow: 0 6px 18px color-mix(in srgb, var(--brand) 40%, transparent);
}

.signin__google {
    display: grid;
    place-items: center;
    min-height: 44px;
}

.signin__loading {
    display: grid;
    place-items: center;
}

/* Blazor's own error strip, restyled to match. */
#blazor-error-ui {
    display: none;
    position: fixed;
    inset-block-end: 0;
    inset-inline: 0;
    z-index: 100;
    padding: var(--space-md) var(--space-xl);
    background: var(--warning-soft);
    color: var(--warning);
    box-shadow: var(--shadow);
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    float: inline-end;
}

/* Announced to a screen reader, invisible to everyone else. */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}
