/* ZenTrip — Apple-style: system type, adaptive light/dark, translucent materials.
 *
 *   · The system font everywhere (SF on Apple hardware). Every size comes from
 *     Apple's Dynamic Type ladder, declared in each page's @theme — and each step
 *     carries its own leading and tracking, so type is never sized on its own.
 *   · Two full palettes behind one set of variables; every colour in the app
 *     resolves through them, so light/dark (and print) are pure re-themes.
 *   · Materials come in weights, and weight is hierarchy: cards are the thick
 *     one (structure — near-opaque, wide blur, deep shadow), the dock and the
 *     day header are the thin one (chrome that floats over them). Thin over
 *     thick, never thin over thin, or the text stops reading.
 *   · Text over any blurred surface gets vibrancy — more contrast, not flat grey.
 *   · Colour still carries meaning: green done/places, purple files, orange now,
 *     red destructive — and one blue tint for actions, the way iOS uses it.
 *   · Feedback is on the press, not the release: every pill and check scales
 *     down on :active, instantly.
 *   · What can be tapped is what can be seen: no control reaches past its own
 *     edges, so a pill's 44px is drawn rather than implied and a label stops
 *     where its box does.
 *
 * Component classes are unlayered, so they beat Tailwind's layered utilities
 * whatever the source order. They must therefore NOT set anything the markup
 * varies (width, padding) or the utility silently loses.
 */

:root {
  color-scheme: light dark;

  --bg: #f5f5f7;
  --bg-2: #ffffff;
  --panel: #ffffff;
  --line: rgb(0 0 0 / 0.12);
  --line-2: rgb(0 0 0 / 0.07);

  --text: #1d1d1f;
  --text-2: #55555a;
  --text-3: #86868b;

  --tint: #0071e3;   /* the one accent: primary actions, focus, links */
  --go:   #1d8a3e;   /* done, places, adding */
  --alt:  #8944ab;   /* files */
  --now:  #c93400;   /* the current time, today */
  --stop: #e30000;   /* destructive */

  --material: rgb(252 252 253 / 0.72);        /* floating chrome (dock) */
  --material-thick: rgb(245 245 247 / 0.9);   /* sheets */
  --edge: rgb(255 255 255 / 1);               /* the lit end of a card's rim */
  /* The diagonal light every material's 1px rim is painted with: lit top-right,
     a faint answer bottom-left. Resolves per palette through --edge/--line-2. */
  --rim: linear-gradient(205deg,
    var(--edge), var(--line-2) 30% 70%,
    color-mix(in srgb, var(--edge) 62%, transparent));
  --field: rgb(120 120 128 / 0.1);
  --shadow-card: 0 1px 2px rgb(0 0 0 / 0.04), 0 10px 30px rgb(0 0 0 / 0.06);
  --shadow-float: 0 12px 34px rgb(0 0 0 / 0.18);

  --ease: cubic-bezier(0.16, 1, 0.3, 1);
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #000000;
    --bg-2: #1c1c1e;
    --panel: #121214;  /* cards sit darker than the chrome — nearer the black ground */
    --line: rgb(255 255 255 / 0.15);
    --line-2: rgb(255 255 255 / 0.08);

    --text: #f5f5f7;
    --text-2: rgb(235 235 245 / 0.64);
    --text-3: rgb(235 235 245 / 0.36);

    --tint: #0a84ff;
    --go:   #30d158;
    --alt:  #bf5af2;
    --now:  #ff9f0a;
    --stop: #ff453a;

    --material: rgb(22 22 24 / 0.72);
    --material-thick: rgb(28 28 30 / 0.9);
    --edge: rgb(255 255 255 / 0.44);
    --field: rgb(120 120 128 / 0.22);
    --shadow-card: 0 1px 2px rgb(0 0 0 / 0.3), 0 10px 30px rgb(0 0 0 / 0.4);
    --shadow-float: 0 12px 34px rgb(0 0 0 / 0.55);
  }
}

/* An app, not a document: no double-tap zoom anywhere. Pinch still zooms. */
body { touch-action: manipulation; }

[x-cloak] {
  display: none !important;
}

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

::selection {
  background: color-mix(in srgb, var(--tint) 24%, transparent);
  color: var(--text);
}

/* ------------------------------------------------------------------ ground */

/* Scroll-edge material under the notch/island: content passes blurred, not raw. */
body::before {
  content: "";
  position: fixed;
  inset: 0 0 auto;
  height: env(safe-area-inset-top);
  z-index: 40;
  pointer-events: none;
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
}

/* Barely-there wash at the top of the page; the ground itself stays calm. */
.sky {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(90rem 44rem at 50% -18%, color-mix(in srgb, var(--tint) 7%, transparent), transparent 70%),
    var(--bg);
}

/* ------------------------------------------------------------------- type */

/* Small section label, shared by both pages. */
.eyebrow {
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-3);
}

/* ---------------------------------------------------------------- entrance */

.reveal {
  opacity: 0;
  animation: rise 0.7s var(--ease) forwards;
}

@keyframes rise {
  from { opacity: 0; transform: translateY(1rem); }
  to   { opacity: 1; transform: none; }
}

/* ------------------------------------------------------------------- card */

/* A surface with a soft, low shadow. The 1px rim is a diagonal light, iOS-widget
   style: lit top-right, a faint answer bottom-left — the border area is painted
   by the rim gradient under the fill. Both are variables so the material weights
   below can change what fills a card without losing its lit edge. */
.card {
  --fill: var(--panel);
  border: 1px solid transparent;
  border-radius: 1.25rem;
  background: linear-gradient(var(--fill), var(--fill)) padding-box, var(--rim) border-box;
  box-shadow: var(--shadow-card);
}

/* ------------------------------------------------------------------- pills */

/* Sets no padding — the markup varies it. Press feedback is instant and on
   pointer-down (:active), never only on release. `manipulation` drops the
   double-tap-to-zoom gesture on controls — which also drops Safari's 300ms wait
   to see whether a second tap is coming — while pinch zoom stays. */
.pill {
  touch-action: manipulation;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
  min-height: 2.75rem; /* the 44px HIG tap minimum, drawn rather than implied */
  border-radius: 999px;
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1.2;
  white-space: nowrap;
  transition: background-color 0.15s, transform 0.12s var(--ease);
}

.pill:active { transform: scale(0.96); }

.pill-tint { color: var(--tint); background: color-mix(in srgb, var(--tint) 12%, transparent); }
.pill-go   { color: var(--go);   background: color-mix(in srgb, var(--go) 12%, transparent); }
.pill-alt  { color: var(--alt);  background: color-mix(in srgb, var(--alt) 12%, transparent); }
.pill-now  { color: var(--now);  background: color-mix(in srgb, var(--now) 13%, transparent); }
.pill-stop { color: var(--stop); background: color-mix(in srgb, var(--stop) 12%, transparent); }
.pill-mute { color: var(--text-2); background: color-mix(in srgb, var(--text-3) 18%, transparent); }

/* A badge states a fact; every pill in this app does something. So it is squared
   off rather than capsule-shaped, outlined rather than filled, and never carries
   the press feedback — it must not read as a control that ignores taps. */
.badge {
  display: inline-flex;
  align-items: center;
  padding: 0.125rem 0.4375rem;
  border: 1px solid color-mix(in srgb, var(--now) 40%, transparent);
  border-radius: 0.375rem;
  color: var(--now);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* Armed destructive: the second tap of a two-step confirm. */
.pill-armed {
  color: #fff;
  background: var(--stop);
  font-weight: 700;
}

.pill-solid {
  color: #fff;
  background: var(--tint);
  font-weight: 600;
}

.pill-solid:disabled { opacity: 0.45; }

/* A separator drawn as light rather than as a border: brightest in the middle,
   gone by both ends, so it divides two zones of a card without ruling a hard line
   across it. Painted as a background image, so it survives a background-colour
   press highlight underneath. */
.rule {
  background-image: linear-gradient(90deg, transparent, var(--line), transparent);
  background-size: 100% 1px;
  background-position: top;
  background-repeat: no-repeat;
}

/* A tappable region of a card — the body that toggles a stop done. It highlights
   under the finger the instant it's pressed, like a table row; without that the
   only thing that answers a tap on the card is the checkbox. */
.tap {
  touch-action: manipulation;
  /* The row is a control, so a tap on it presses rather than selecting the words
     inside it — and no blue flash on the way through. */
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  transition: background-color 0.15s;
}

.tap:active { background-color: color-mix(in srgb, var(--text) 8%, transparent); }

/* On a mouse the press highlight gets a resting cousin: a wash of the control's
   own colour, so one rule fits every tint. The checkmark image is left alone —
   and so is glass, whose gradient lives in background-image; a glass pill takes
   its wash as a background-colour underneath instead. */
@media (hover: hover) {
  :is(.pill, .dock-tab, .dock-add):not(.glass):hover {
    background-image: linear-gradient(
      color-mix(in srgb, currentColor 8%, transparent),
      color-mix(in srgb, currentColor 8%, transparent));
  }

  .pill.glass:hover { background-color: color-mix(in srgb, currentColor 6%, transparent); }

  /* No hover on .tap regions: a card body highlighting under a resting cursor
     reads as noise — the press highlight is feedback enough. */
  .check:not(:checked, :disabled):hover { border-color: var(--text-2); }
}

/* --------------------------------------------------------------- controls */

/* Round, tinted when checked — still a real <input> so labels and keyboard
   work, still the nicest thing to hit with a thumb. */
.check {
  appearance: none;
  touch-action: manipulation;
  flex: none;
  /* Drawn at the 44px HIG target rather than tapped at it: a small circle with
     a big invisible hit area reads as a smaller control than it is. */
  width: 2.75rem;
  height: 2.75rem;
  border: 2px solid color-mix(in srgb, var(--text-3) 65%, transparent);
  border-radius: 999px;
  cursor: pointer;
  transition: background-color 0.15s, border-color 0.15s, transform 0.12s var(--ease);
}

.check:active { transform: scale(0.9); }

.check:checked {
  border-color: var(--go);
  background: var(--go) url("data:image/svg+xml,%3Csvg viewBox='0 0 16 16' fill='none' stroke='%23fff' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M3.5 8.5l3 3 6-6'/%3E%3C/svg%3E") center / 1.5rem no-repeat;
}

.check:disabled { cursor: default; opacity: 0.55; }

/* iOS-style fill fields: a quiet tinted well, no chrome until focus. Owns its
   size — Callout, the same in every field — which is also what stops Safari
   zooming the page in on focus, something it does to any field under 16px. */
.field {
  font-size: 1rem;
  background: var(--field);
  border: 1px solid transparent;
  border-radius: 0.75rem;
  color: var(--text);
  padding: 0.6875rem 0.875rem;
  transition: border-color 0.15s, background-color 0.15s, box-shadow 0.15s;
}

.field::placeholder { color: var(--text-3); }

/* The macOS focus ring — the same glow the create bar wears, on every field. */
.field:focus {
  outline: none;
  border-color: var(--tint);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--tint) 14%, transparent);
}

/* iOS sizes date/time inputs at their intrinsic width, overflowing the shared
   row on phones; without the native appearance they size like any field. */
.field[type="date"],
.field[type="time"] {
  -webkit-appearance: none;
  appearance: none;
  min-width: 0;
}

/* ------------------------------------------------------- floating nav pill */

.dock-wrap {
  position: fixed;
  bottom: max(1.25rem, env(safe-area-inset-bottom));
  left: 50%;
  transform: translateX(-50%);
  z-index: 50;
  display: flex;
  align-items: center;
  gap: 0.625rem;
}

/* One of the two true materials: it floats over scrolling content, so the
   blur actually has something to blur. */
.dock {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.375rem;
  border-radius: 999px;
  background: var(--material);
  border: 1px solid var(--line-2);
  box-shadow: var(--shadow-float);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
}

/* Add stands apart from the tabs, the way iOS floats an action beside the bar. */
.dock-add {
  touch-action: manipulation;
  display: grid;
  place-items: center;
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 999px;
  color: #fff;
  background: var(--tint);
  box-shadow: var(--shadow-float);
  transition: transform 0.12s var(--ease);
}

.dock-add:active { transform: scale(0.94); }

.dock-tab {
  touch-action: manipulation;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 2.75rem;
  padding: 0 0.9375rem;
  border-radius: 999px;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--text-2);
  transition: color 0.2s, background-color 0.2s, transform 0.12s var(--ease);
}

.dock-tab:active { transform: scale(0.96); }

.dock-tab[aria-selected="true"] {
  color: var(--tint);
  background: color-mix(in srgb, var(--text) 10%, transparent);
}

/* ----------------------------------------------------------- liquid glass */

/* The thin material: what floats over content — the dock, the day header. Heavy
   saturated blur, a specular sheen, a lit edge. Declared after the pills and
   the dock so it wins their background, border and shadow. */
.glass {
  background: linear-gradient(200deg,
    rgb(255 255 255 / 0.42), rgb(255 255 255 / 0.3) 45%, rgb(255 255 255 / 0.36));
  border: 1px solid rgb(255 255 255 / 0.28);
  box-shadow:
    inset 0 1px 1px rgb(255 255 255 / 0.35),
    inset 0 -1px 1px rgb(255 255 255 / 0.08),
    0 14px 38px rgb(0 0 0 / 0.22);
  backdrop-filter: blur(12px) saturate(200%);
  -webkit-backdrop-filter: blur(12px) saturate(200%);
}

@media (prefers-color-scheme: dark) {
  .glass {
    background: linear-gradient(200deg,
      rgb(255 255 255 / 0.16), rgb(255 255 255 / 0.1) 45%, rgb(255 255 255 / 0.13));
    border-color: rgb(255 255 255 / 0.12);
    box-shadow:
      inset 0 1px 1px rgb(255 255 255 / 0.14),
      inset 0 -1px 1px rgb(255 255 255 / 0.04),
      0 14px 38px rgb(0 0 0 / 0.5);
  }
}

/* The thick material: a card is a big structural surface, so it reads heavier
   than the chrome floating over it — a translucent fill behind the same lit rim
   every card wears, a wider blur, the card's own quiet shadow. Thin glass over
   thin glass would leave neither legible. */
.card.glass {
  --fill: color-mix(in srgb, var(--panel) 80%, transparent);
  border-color: transparent;
  background: linear-gradient(var(--fill), var(--fill)) padding-box, var(--rim) border-box;
  box-shadow: var(--shadow-card);
  backdrop-filter: blur(28px) saturate(180%);
  -webkit-backdrop-filter: blur(28px) saturate(180%);
}

/* The day banner. It wears the material at rest too, so only its corners move
   when it pins. */
.day-head {
  transition: border-radius 0.3s var(--ease);
}

/* The thin material — it floats over the thick cards — worn as a scroll edge
   rather than a hard divider, so content passes under it blurred. */
.day-edge {
  /* Mostly what's behind it, with enough frost in the gradient itself to give
     the banner a body — in light mode the tint IS the white, so it lives here
     rather than in a panel-coloured layer that would be white on white. */
  background: linear-gradient(200deg,
    rgb(255 255 255 / 0.62), rgb(255 255 255 / 0.34) 45%, rgb(255 255 255 / 0.5));
  /* The rim, said in box-shadow rather than a border: it must ring the banner
     evenly — the card's diagonal rim fades out in the middle, which over a
     translucent surface reads as no edge at all. */
  box-shadow:
    inset 0 1px 1px rgb(255 255 255 / 0.45),
    inset 0 0 0 1px rgb(255 255 255 / 0.3),
    0 6px 20px rgb(0 0 0 / 0.08);
  backdrop-filter: blur(28px) saturate(220%);
  -webkit-backdrop-filter: blur(28px) saturate(220%);
}

@media (prefers-color-scheme: dark) {
  .day-edge {
    background:
      linear-gradient(200deg,
        rgb(255 255 255 / 0.18), rgb(255 255 255 / 0.06) 45%, rgb(255 255 255 / 0.12)),
      color-mix(in srgb, var(--panel) 55%, transparent);
    box-shadow:
      inset 0 1px 1px rgb(255 255 255 / 0.14),
      inset 0 0 0 1px rgb(255 255 255 / 0.09),
      0 6px 20px rgb(0 0 0 / 0.35);
  }
}

/* Vibrancy: secondary text loses contrast the moment the surface under it moves,
   so over any material — glass or the pinned banner — it steps up rather than
   staying flat grey. */
.glass, .day-edge {
  --text-2: #3c3c42;
  --text-3: #64646b;
}

@media (prefers-color-scheme: dark) {
  .glass, .day-edge {
    --text-2: rgb(235 235 245 / 0.78);
    --text-3: rgb(235 235 245 / 0.5);
  }
}

/* ------------------------------------------------------------------ sheet */

/* The other true material. Markup owns size, radius and padding.
 *
 * The closed state is the resting rule and `[open]` is the exception, so the
 * sheet leaves along the path it arrived by instead of being switched off — the
 * discrete `display`/`overlay` transitions are what keep it on screen long
 * enough to animate out. And it materialises rather than fades: the blur and
 * the scale come up with the opacity, so it reads as glass arriving. */
.sheet {
  background: var(--material-thick);
  border: 1px solid var(--line-2);
  box-shadow: var(--shadow-float);
  overscroll-behavior: contain;
  backdrop-filter: blur(24px) saturate(180%);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  transition:
    translate 0.5s var(--ease), scale 0.5s var(--ease), opacity 0.5s var(--ease),
    backdrop-filter 0.5s var(--ease), -webkit-backdrop-filter 0.5s var(--ease),
    display 0.5s allow-discrete, overlay 0.5s allow-discrete;
}

/* Guarded, because the hidden state is only safe where the browser can transition
   `display`: without that this would be a sheet that opens invisible. */
@supports (transition-behavior: allow-discrete) {
  .sheet {
    translate: 0 2.5rem;
    scale: 0.98;
    opacity: 0;
    backdrop-filter: blur(0) saturate(180%);
    -webkit-backdrop-filter: blur(0) saturate(180%);
  }

  .sheet[open] {
    translate: none;
    scale: 1;
    opacity: 1;
    backdrop-filter: blur(24px) saturate(180%);
    -webkit-backdrop-filter: blur(24px) saturate(180%);
  }

  dialog::backdrop { opacity: 0; }
  dialog[open]::backdrop { opacity: 1; }
}

/* The frame the entrance starts from: sheets rise like iOS instead of popping in. */
@starting-style {
  .sheet[open] {
    translate: 0 2.5rem;
    scale: 0.98;
    opacity: 0;
    backdrop-filter: blur(0) saturate(180%);
    -webkit-backdrop-filter: blur(0) saturate(180%);
  }

  dialog[open]::backdrop { opacity: 0; }
}

/* The page holds still while a sheet is up; only the sheet itself scrolls. */
body:has(dialog[open]) { overflow: hidden; }

/* iOS grabber — phones only; desktop shows a centered modal, not a sheet. */
.sheet::before {
  content: "";
  position: absolute;
  top: 0.375rem;
  left: 50%;
  translate: -50%;
  width: 2.25rem;
  height: 0.3125rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--text-3) 40%, transparent);
}

@media (min-width: 640px) {
  .sheet::before { display: none; }
}

/* Same shape as the sheet: the scrim's closed state lives in the @supports block
   above, so it fades out with the sheet rather than blinking off. */
dialog::backdrop {
  background: rgb(0 0 0 / 0.4);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  transition: opacity 0.5s, display 0.5s allow-discrete, overlay 0.5s allow-discrete;
}

@media (prefers-color-scheme: dark) {
  dialog::backdrop { background: rgb(0 0 0 / 0.6); }
}

/* The hairline between two stops: it runs the full width of the card but comes
   up out of nothing at both ends, so it reads as a seam in the list rather than
   a rule butting into the card's edges. */
.row-line {
  position: relative;
}

.row-line::before {
  content: "";
  position: absolute;
  z-index: 1;
  top: 0;
  right: -1rem;
  left: -1rem;
  height: 1px;
  background: linear-gradient(to right,
    transparent, var(--line-2) 22%, var(--line-2) 78%, transparent);
}

/* ------------------------------------------------------------ now marker */

.now-dot {
  box-shadow: 0 0 12px color-mix(in srgb, var(--now) 50%, transparent);
}

/* The rule reads as a horizon the dot sits on, not a divider: a hairline that
   thins to nothing before it reaches the card's edge, so it marks the moment
   without cutting the day in two. */
.now-rule {
  height: 1px;
  background: linear-gradient(to right,
    var(--now), color-mix(in srgb, var(--now) 55%, transparent) 80%, transparent);
}

/* Sets no `position`: the element is already absolutely positioned by the markup,
   and this class is unlayered so a `position` here would outrank that. */
.now-dot::after {
  content: "";
  position: absolute;
  inset: -3px;
  border-radius: 999px;
  border: 2px solid var(--now);
  animation: now-pulse 2.6s ease-out infinite;
}

/* transform + opacity only, so it never triggers a repaint. */
@keyframes now-pulse {
  from { transform: scale(1); opacity: 0.55; }
  to   { transform: scale(2.1); opacity: 0; }
}

/* ----------------------------------------------------------- preferences */

/* Reduced motion is not no feedback: what travels across the screen goes, what
   explains the interface — colour, opacity, the press highlight — stays. So the
   transition list is narrowed to the non-vestibular properties rather than the
   durations being zeroed, which used to take the explanations with them. */
@media (prefers-reduced-motion: reduce) {
  .now-dot::after { animation: none; }
  .reveal { animation: none; opacity: 1; }

  /* `display`/`overlay` stay in the list, or the sheet would blink out instead
     of cross-fading — the exit needs them to transition discretely. */
  *, *::before, *::after {
    transition-property: background-color, border-color, color, opacity, box-shadow, display, overlay !important;
    transition-behavior: allow-discrete !important;
  }

  /* Sheets cross-fade in place instead of rising. */
  .sheet, .sheet[open] { translate: none; scale: 1; }
}

/* Frostier, then solid: translucency is a garnish, never load-bearing. */
@media (prefers-reduced-transparency: reduce) {
  .dock, .sheet, .glass, .card.glass, .day-edge {
    background: var(--bg-2);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

  body::before { display: none; }
}

@media (prefers-contrast: more) {
  .dock, .sheet, .card, .card.glass, .field {
    border-color: var(--text-3);
  }
}

/* --------------------------------------------------------------- printing */

/* An itinerary is a travel document; it should survive a printer. Every colour
   resolves through a variable, so redefining them here re-themes the page —
   including for someone printing from dark mode. */
@media print {
  :root {
    --bg: #fff;
    --bg-2: #fff;
    --panel: #fff;
    --line: #c9c9c9;
    --line-2: #e2e2e2;
    --text: #000;
    --text-2: #333;
    --text-3: #555;
    --tint: #0b57a4;
    --go: #0f7a56;
    --alt: #4c3a99;
    --now: #a3521a;
    --stop: #99303f;
  }

  .sky,
  .dock,
  [data-print-hide] { display: none !important; }

  .card, .card.glass {
    background: var(--panel);
    border-color: var(--line-2);
    box-shadow: none;
    backdrop-filter: none;
    break-inside: avoid;
  }

  .now-dot::after { animation: none; }
}
