/* AUTO-GENERATED by ui-tokens build.rs. Do not edit. */

:root {
  --space-0: 0;
  --space-0-5: 4px;
  --space-1: 8px;
  --space-2: 16px;
  --space-3: 24px;
  --space-4: 32px;
  --space-6: 48px;
  --space-8: 64px;
  --radius-block: 2px;
  --radius-lg: 16px;
  --radius-md: 8px;
  --radius-none: 0;
  --radius-pill: 9999px;
  --radius-sm: 4px;
  --border-hairline: 1px;
  --border-strong: 2px;
  --shadow-md: 0 4px 12px rgba(22, 48, 62, 0.10);
  --shadow-none: none;
  --shadow-sm: 0 1px 2px rgba(22, 48, 62, 0.10);
  --text-2xl: 28px;
  --text-3xl: 36px;
  --text-base: 16px;
  --text-lg: 18px;
  --text-sm: 14px;
  --text-sm-dense: 13px;
  --text-xl: 22px;
  --text-xs: 12px;
  --weight-body: 400;
  --weight-display: 600;
  --weight-ui: 500;
  --leading-body: 1.5;
  --leading-heading: 1.25;
  --leading-mono: 1;
  --motion-base: 200ms;
  --motion-fast: 120ms;
  --motion-reduced: 90ms;
  --motion-slow: 480ms;
  --ease-overshoot: cubic-bezier(0.34, 1.56, 0.64, 1);
  --ease-standard: cubic-bezier(0.2, 0, 0.2, 1);
  --cal-day-col-w: 132px;
  --cal-day-col-w-narrow: 106px;
  --cal-row-h: 50px;
  --cal-row-h-narrow: 44px;
  --cal-ruler-h: 26px;
  --cal-tide-h: 34px;
  --customer-badge-w: 118px;
  --customer-phone-w: 58px;
  --customer-stats-w: 226px;
  --font-family: "IBM Plex Sans", system-ui, sans-serif;
  --font-ui: "IBM Plex Sans", system-ui, sans-serif;
  --font-display: Besley, Georgia, serif;
  --font-mono: "IBM Plex Mono", ui-monospace, monospace;
}

:root[data-theme="dark"], [data-theme="dark"] {
  --accent: #4FB6C9;
  --accent-hover: #66C2D4;
  --bg-base: #0B1B26;
  --bg-elevated: #142B3B;
  --bg-inverse: #F2EEE2;
  --bg-sunken: #071219;
  --border-default: #4A6B7C;
  --border-strong: #8AA4B2;
  --danger: #E58273;
  --danger-inverse: #B3402F;
  --elevation-edge: #3D617A;
  --elevation-glow: #FFFFFF12;
  --fg-inverse: #0B1B26;
  --fg-muted: #A3B8C2;
  --fg-on-accent: #06222B;
  --fg-primary: #F2EEE2;
  --fg-secondary: #B7C9D1;
  --focus-ring: #6CC4DE;
  --free-tint-1: #183545;
  --free-tint-2: #204757;
  --free-tint-3: #285A6B;
  --info: #6FB4E8;
  --info-inverse: #1C5D8D;
  --signal: #F06CA8;
  --signal-inverse: #B91C5B;
  --status-cancelled: #95A4AA;
  --status-cancelled-tint: #1A2228;
  --status-held: #84B9DF;
  --status-held-tint: #102A3D;
  --status-paid: #8FD4AC;
  --status-paid-tint: #103526;
  --status-unpaid: #E6BE7A;
  --status-unpaid-tint: #362A12;
  --success: #6FBF8F;
  --success-inverse: #2E6E4E;
  --taken-tint: #364D5B;
  --warning: #E0A458;
  --warning-inverse: #8A5A19;
}

:root[data-theme="high-contrast"], [data-theme="high-contrast"] {
  --accent: #003B4A;
  --accent-hover: #002A36;
  --bg-base: #FFFFFF;
  --bg-elevated: #FFFFFF;
  --bg-inverse: #FFFFFF;
  --bg-sunken: #EFEFEA;
  --border-default: #0A0F12;
  --border-strong: #0A0F12;
  --danger: #8F1D0E;
  --danger-inverse: #8F1D0E;
  --elevation-edge: #00000000;
  --elevation-glow: #00000000;
  --fg-inverse: #0A0F12;
  --fg-muted: #2E3A40;
  --fg-on-accent: #FFFFFF;
  --fg-primary: #0A0F12;
  --fg-secondary: #2E3A40;
  --focus-ring: #0A0F12;
  --free-tint-1: #FFFFFF;
  --free-tint-2: #FFFFFF;
  --free-tint-3: #FFFFFF;
  --info: #00457C;
  --info-inverse: #00457C;
  --signal: #0A0F12;
  --signal-inverse: #0A0F12;
  --status-cancelled: #3A3A3A;
  --status-cancelled-tint: #FFFFFF;
  --status-held: #00457C;
  --status-held-tint: #FFFFFF;
  --status-paid: #0E5A2E;
  --status-paid-tint: #FFFFFF;
  --status-unpaid: #6B4400;
  --status-unpaid-tint: #FFFFFF;
  --success: #0E5A2E;
  --success-inverse: #0E5A2E;
  --taken-tint: #FFFFFF;
  --warning: #6B4400;
  --warning-inverse: #6B4400;
}

:root[data-theme="light"], [data-theme="light"] {
  --accent: #0F4C5C;
  --accent-hover: #0B3A47;
  --bg-base: #F8F4E9;
  --bg-elevated: #FDFBF3;
  --bg-inverse: #16303E;
  --bg-sunken: #E9F0EC;
  --border-default: #A8B5AD;
  --border-strong: #5C7280;
  --danger: #B3402F;
  --danger-inverse: #E58273;
  --elevation-edge: #00000000;
  --elevation-glow: #00000000;
  --fg-inverse: #EDE8DA;
  --fg-muted: #5C7280;
  --fg-on-accent: #FFFFFF;
  --fg-primary: #16303E;
  --fg-secondary: #4A6273;
  --focus-ring: #20566B;
  --free-tint-1: #ECEFE8;
  --free-tint-2: #CDD8D5;
  --free-tint-3: #ACC0C0;
  --info: #1C5D8D;
  --info-inverse: #6FB4E8;
  --signal: #B91C5B;
  --signal-inverse: #F06CA8;
  --status-cancelled: #4E5D5D;
  --status-cancelled-tint: #E8E6DD;
  --status-held: #1C5D8D;
  --status-held-tint: #DCE9F2;
  --status-paid: #1E5A40;
  --status-paid-tint: #DCECE2;
  --status-unpaid: #7E5114;
  --status-unpaid-tint: #F3E6CE;
  --success: #2E6E4E;
  --success-inverse: #6FBF8F;
  --taken-tint: #D6DAD7;
  --warning: #8A5A19;
  --warning-inverse: #E0A458;
}


/* The calendar sea's strip, as tall as a `seaband` band. */
:root {
  --sea-strip-h: 88px;
}

/* The calendar on its side (DESIGN.md, Direction, 2026-09-28): days run down, the
   hours 00–24 run across. Written once here and appended to theme.css by build.rs, so
   the admin and customer apps draw the same rows. The ruler and every row share one
   grid template, so the hour labels line up with the tracks. A container query cannot
   style its own container, so the narrow sizes are set on the rows, not on .cal-rows. */
.cal-rows { container-type: inline-size; display: grid; row-gap: var(--space-1); }
.cal-ruler, .cal-row { --cal-daycol: var(--cal-day-col-w); --cal-rowh: var(--cal-row-h); display: grid; grid-template-columns: var(--cal-daycol) minmax(0, 1fr); }
.cal-ruler { height: var(--cal-ruler-h); align-items: end; }
.cal-ruler-day { padding-bottom: 6px; font-size: var(--text-xs); font-weight: var(--weight-display); letter-spacing: 0.08em; text-transform: uppercase; color: var(--fg-secondary); }
/* The labels are placed across the track's inner width, inside its border, the frame the
   hour lines use, so each sits 3 px right of its own line from 00 to 23. */
.cal-ruler-hours { position: relative; height: 100%; margin-inline: var(--border-hairline); }
.cal-hour { position: absolute; bottom: 4px; transform: translateX(3px); font-family: var(--font-mono); font-size: var(--text-xs); color: var(--fg-muted); }
/* Mono numerals stop at Medium: both shells ship Plex Mono at 400 and 500 only, so a
   600 would be a bold the browser fakes and Slint cannot draw. */
.cal-hour.major { color: var(--fg-secondary); font-weight: var(--weight-ui); }

/* The day label: one line at every width, like a departures board. */
.cal-day { display: grid; align-content: center; gap: 3px; min-width: 0; padding-right: var(--space-1); }
.cal-dl { display: flex; align-items: baseline; gap: 7px; white-space: nowrap; line-height: 1; }
.cal-dl-tag { font-size: 11px; font-weight: var(--weight-display); letter-spacing: 0.06em; text-transform: uppercase; color: var(--accent); }
.cal-dl-wd { font-size: 13px; font-weight: var(--weight-display); letter-spacing: 0.08em; text-transform: uppercase; }
.cal-dl-dt { font-family: var(--font-mono); font-size: 20px; }
.cal-dl-clock { font-size: 10px; font-weight: var(--weight-display); color: var(--warning); border: 1px solid var(--warning); border-radius: 3px; padding: 2px 4px; }
.cal-day.today .cal-dl-wd, .cal-day.today .cal-dl-dt { color: var(--accent); }
.cal-tally { font-size: var(--text-xs); line-height: 1.2; color: var(--fg-secondary); white-space: nowrap; }
.cal-day.full .cal-tally { color: var(--fg-muted); }
.cal-cap { display: block; height: 4px; border-radius: 2px; background: var(--bg-sunken); overflow: hidden; }
.cal-cap > i { display: block; height: 100%; background: var(--accent); }

/* The track: hour hairlines, every third hour stronger. */
.cal-track {
  position: relative; height: var(--cal-rowh); overflow: hidden; align-self: center;
  background-color: var(--bg-base); border: var(--border-hairline) solid var(--border-default); border-radius: var(--radius-md);
  background-image:
    repeating-linear-gradient(to right, var(--border-default) 0 1px, transparent 1px calc(100% / 8)),
    repeating-linear-gradient(to right, color-mix(in srgb, var(--border-default) 45%, transparent) 0 1px, transparent 1px calc(100% / 24));
}

/* Free time: an open slot with a dashed accent outline that says how many sessions fit.
   A gap too short for one is a plain dotted box with no text. */
.cal-free, .cal-short { position: absolute; top: 0; bottom: 0; z-index: 0; padding: 0; border-radius: var(--radius-block); font: inherit; }
.cal-free { display: grid; align-content: center; padding: 0 var(--space-1); overflow: hidden; text-align: left; cursor: pointer;
  background: var(--free-tint-1); border: 1px dashed var(--accent); color: var(--accent); line-height: 1.25; }
.cal-free > * { pointer-events: none; white-space: nowrap; }
.cal-free b { font-size: var(--text-sm); font-weight: var(--weight-display); }
.cal-free b .mono { font-weight: var(--weight-ui); }
.cal-free small { font-size: var(--text-xs); color: var(--fg-secondary); }
.cal-free:hover, .cal-free.f2 { background: var(--free-tint-2); color: var(--fg-primary); }
.cal-free.f3 { background: var(--free-tint-3); color: var(--fg-primary); }
.cal-free:hover small, .cal-free.f2 small, .cal-free.f3 small { color: var(--fg-primary); }
.cal-short { background: var(--bg-sunken); border: 1px dotted var(--border-strong); cursor: help; }
/* Both apps' `button:hover` would fill a too-short gap with accent; it stays a dotted box. */
.cal-short:hover { background: var(--bg-sunken); }
/* A piece that runs over midnight: square and dashed on the cut side. Scoped to the
   track because the apps' own single-class block rules load after theme.css and
   would win a tie, putting the rounded corners back. */
.cal-track .cut-l { border-top-left-radius: 0; border-bottom-left-radius: 0; border-left-style: dashed; }
.cal-track .cut-r { border-top-right-radius: 0; border-bottom-right-radius: 0; border-right-style: dashed; }

/* Marks over the track: the hatched past, the clock-change hour, the now-line. */
.cal-past { position: absolute; top: 0; bottom: 0; left: 0; z-index: 2; pointer-events: none;
  background: repeating-linear-gradient(135deg, color-mix(in srgb, var(--fg-muted) 22%, transparent) 0 1px, transparent 1px 6px); }
.cal-clock { position: absolute; top: 0; bottom: 0; z-index: 2; pointer-events: none;
  background: repeating-linear-gradient(90deg, color-mix(in srgb, var(--warning) 30%, transparent) 0 1px, transparent 1px 4px);
  border-inline: 1px dotted var(--warning); }
.cal-clock-note { margin: 0; font-size: var(--text-sm); color: var(--warning); }
/* The now-line is centred on the instant: it is shifted left by half its width, and its
   point, placed from the line's padding edge, moves with it. */
.cal-now { --cal-now-w: 2px; position: absolute; top: 0; bottom: 0; width: 0; z-index: 3; pointer-events: none;
  border-left: var(--cal-now-w) solid var(--signal); margin-left: calc(var(--cal-now-w) / -2); }
.cal-now::before { content: ""; position: absolute; top: -1px; left: -5px; border-top: 6px solid var(--signal); border-left: 4px solid transparent; border-right: 4px solid transparent; }
:root[data-theme="high-contrast"] .cal-now { --cal-now-w: 3px; border-left-color: var(--fg-primary); }
:root[data-theme="high-contrast"] .cal-now::before { border-top-color: var(--fg-primary); }

/* Dag: the tide curve in a strip between the ruler and the row. */
.cal-tide { position: relative; height: var(--cal-tide-h); pointer-events: none; }
.cal-tide svg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.cal-tide path.tide-fill { fill: color-mix(in srgb, var(--accent) 10%, transparent); }
.cal-tide path.tide-line { fill: none; stroke: color-mix(in srgb, var(--accent) 55%, transparent); stroke-width: 1.25; vector-effect: non-scaling-stroke; }

/* The day as a list: Dag under its row, and the members' phone week. */
.cal-list { display: grid; gap: var(--space-0-5); }
.cal-daylist { display: none; }
.cal-entry { display: grid; grid-template-columns: 11ch minmax(0, 1fr) auto; gap: 2px var(--space-1); align-items: center; width: 100%;
  padding: var(--space-1); text-align: left; font: inherit; color: var(--fg-primary); cursor: pointer;
  background: var(--bg-base); border: 1px solid var(--border-default); border-radius: var(--radius-sm); }
.cal-entry > * { pointer-events: none; }
.cal-entry-time { font-family: var(--font-mono); font-size: var(--text-sm); }
.cal-entry-what { display: grid; gap: 1px; min-width: 0; }
.cal-entry-what small { font-size: var(--text-xs); color: var(--fg-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cal-entry-go { font-size: var(--text-sm); font-weight: var(--weight-display); color: var(--accent); white-space: nowrap; }
.cal-entry.paid { background: var(--status-paid-tint); border-left: 4px solid var(--status-paid); }
.cal-entry.unpaid { background: var(--status-unpaid-tint); border-left: 4px solid var(--status-unpaid); }
.cal-entry.held, .cal-entry.own { background: var(--status-held-tint); border-left: 4px solid var(--status-held); }
.cal-entry.own.paid { background: var(--status-paid-tint); border-left-color: var(--status-paid); }
.cal-entry.own.unpaid { background: var(--status-unpaid-tint); border-left-color: var(--status-unpaid); }
.cal-entry.cancelled { background: var(--status-cancelled-tint); border-left: 4px solid var(--status-cancelled); }
.cal-entry.taken { background: var(--taken-tint); border-left: 4px solid var(--border-strong); cursor: default; }
.cal-entry.dimmed { opacity: 0.4; }
.cal-entry.is-free { background: var(--free-tint-1); border: 1px dashed var(--accent); }
.cal-entry.is-free.f2 { background: var(--free-tint-2); }
.cal-entry.is-free.f3 { background: var(--free-tint-3); }
/* Text on the deeper tints is fg-primary, the pair the AA gate checks. */
:is(.cal-entry.is-free.f2, .cal-entry.is-free.f3, .cal-entry.taken) :is(.cal-entry-what small, .cal-entry-go) { color: var(--fg-primary); }
.cal-entry.is-short { background: var(--bg-sunken); border: 1px dotted var(--border-strong); cursor: help; }

/* The key under the toolbar. Each swatch carries the classes the calendar draws with, so
   it has the same fill, outline and buoy; this only takes it out of the track's layout.
   The line wraps on a phone rather than scroll. */
.cal-legend { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-0-5) var(--space-2); margin-bottom: var(--space-1);
  font-size: var(--text-xs); color: var(--fg-secondary); }
.cal-legend-item { display: inline-flex; align-items: center; gap: var(--space-0-5); white-space: nowrap; }
.cal-legend .cal-legend-swatch { position: relative; display: inline-block; flex: none; width: 28px; height: 16px; padding: 0; z-index: auto; pointer-events: none; }

@container (max-width: 820px) {
  .cal-ruler, .cal-row { --cal-daycol: var(--cal-day-col-w-narrow); --cal-rowh: var(--cal-row-h-narrow); }
  .cal-hour:not(.major), .cal-dl-clock { display: none; }
  .cal-dl { gap: 5px; }
  .cal-dl-wd { font-size: 12px; letter-spacing: 0.05em; }
  .cal-dl-dt { font-size: 16px; }
}
@container (max-width: 520px) { .cal-hour.major:not(.six) { display: none; } }

/* Signalen, the toast (DESIGN.md, Signalen, 2026-10-05). Written once here and appended
   to theme.css by build.rs, so the admin and customer apps draw the same card; the
   desktop draws it in crates/ui-shared/lib/components/toast.slint. The card is the
   inverse of the screen behind it and severity sits in a solid tile. The lifetime line's
   animation is the dismissal clock, so hovering the card pauses both. */

/* The strip sits in the flow straight under the top bar and sticks to the top once the
   bar scrolls away. It takes no room; the column hangs 16 px below it, centred. */
.signalen-stack { position: sticky; top: 0; height: 0; z-index: 100; }
/* `max-content`, or the browser gives an absolutely placed box starting at 50 % only
   the other half of the window, and a long message wraps at 400 px on a laptop. */
.signalen-col {
  position: absolute; top: var(--space-2); left: 50%; transform: translateX(-50%);
  width: max-content; display: flex; flex-direction: column; gap: var(--space-1);
}
.signalen {
  --signalen-room: calc(100vw - 2 * var(--space-2));
  position: relative; overflow: hidden; box-sizing: border-box;
  display: flex; align-items: center; gap: 12px;
  min-width: min(400px, var(--signalen-room)); max-width: min(560px, var(--signalen-room));
  padding: 12px 12px 15px;
  background: var(--bg-inverse); color: var(--fg-inverse);
  border-radius: var(--radius-md); box-shadow: var(--shadow-md);
  animation: signal-in var(--motion-base) var(--ease-standard) backwards,
    signal-ping 600ms ease-out var(--motion-base);
}
@keyframes signal-in { from { opacity: 0; transform: translateY(-16px); } }
/* "Ankaret i": one magenta ring spreads 10 px from the card and fades. The card's own
   shadow rides in both frames so the ring does not wipe it. */
@keyframes signal-ping {
  from { box-shadow: var(--shadow-md), 0 0 0 0 var(--signal-inverse); }
  to { box-shadow: var(--shadow-md), 0 0 0 10px transparent; }
}
.signalen.danger { --sev: var(--danger); --sev-inverse: var(--danger-inverse); }
.signalen.info { --sev: var(--info); --sev-inverse: var(--info-inverse); }
.signalen.success { --sev: var(--success); --sev-inverse: var(--success-inverse); }
.signalen.warning { --sev: var(--warning); --sev-inverse: var(--warning-inverse); }
/* The tile: the card is the other theme's surface, so it takes the other theme's
   severity colour, 4.5:1 or better on the card (build.rs checks it), and holds the
   flag in the card's own colour. */
.signalen-flag {
  flex-shrink: 0; display: grid; place-items: center; width: 36px; height: 36px;
  border-radius: var(--radius-sm); color: var(--bg-inverse); background: var(--sev-inverse);
}
.signalen-flag svg { width: 20px; height: 20px; display: block; }
/* The text may shrink and break anywhere, so a long address on a phone cannot push the
   close control off a sticky error, which would then have no way out. */
.signalen-text {
  flex: 1; min-width: 0; overflow-wrap: anywhere;
  font-size: var(--text-lg); font-weight: var(--weight-ui); line-height: var(--leading-heading);
}
/* The severity word, heard before the message and never seen. */
.signalen-severity {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
.signalen-close {
  flex-shrink: 0; width: 32px; height: 32px; padding: 0;
  background: transparent; border: none; border-radius: var(--radius-sm);
  color: var(--fg-inverse); font-size: var(--text-base);
}
.signalen-close:hover { background: transparent; }
/* The focus-ring token fails 3:1 on the light theme's card, so the ring takes the
   card's own ink. */
.signalen-close:focus-visible { outline-color: var(--fg-inverse); }
.signalen-life {
  position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
  background: var(--signal-inverse); transform-origin: left;
  animation: signal-life var(--signalen-dwell) linear forwards;
}
/* `!important`, because the reduced-motion rules below set the whole `animation`
   shorthand with it, play state included, and a reader who asked for less motion is
   the one most likely to need the pause. */
.signalen:hover .signalen-life { animation-play-state: paused !important; }
@keyframes signal-life { to { transform: scaleX(0); } }
/* Reduced motion stops every animation, and this one is the clock: without it a toast
   would never leave. It keeps running in one step, so nothing moves until it goes. */
@media (prefers-reduced-motion: reduce) {
  .signalen-life { animation: signal-life var(--signalen-dwell) steps(1, end) forwards !important; }
}
[data-reduce-motion] .signalen-life {
  animation: signal-life var(--signalen-dwell) steps(1, end) forwards !important;
}
/* High contrast: the card is white on white, so it carries a 2 px frame in the raw
   severity colour. Its signal-inverse line is already ink there (spec §2 bans magenta). */
:root[data-theme="high-contrast"] .signalen { border: 2px solid var(--sev); }
