/* KitchenDrop brand foundation — tokens, type, and primitives.
   Loaded by every page before its own stylesheet. */

/* ---------- Colour ----------
   Taken from the flame mark: ember orange is the brand, deep navy is the ink
   and the counterweight, on warm paper rather than a cool grey. The two logo
   colours do different jobs — orange carries action and emphasis, navy carries
   text, surfaces and anything that must not compete with a button. */

:root,
[data-theme="light"] {
  --brand: #dd5a1f;
  --brand-strong: #b8431a;
  --brand-wash: #fdeee4;
  /* The brand as text. The ember itself is 3.78:1 on white -- a fill, an
     icon or a large heading, but not a 13px label (rule 14). Text takes the
     deeper shade, which clears AA on every light surface. Dark mode's ember
     already clears it, so there the two are the same. */
  --brand-ink: #b8431a;
  /* The ink that goes on top of a brand fill. Light mode paints white on the
     ember; dark mode brightens the ember far enough that white stops clearing
     contrast and the label has to flip to the deep navy instead. That flip was
     written inside .btn-primary and nowhere else, so the one primary chat chip
     in the app never got it and kept white text on bright orange. */
  --on-brand: #ffffff;

  /* The mark's second colour. Used where a badge or meter has to read as
     distinct from the brand orange instead of blending into it. */
  --navy: #2c3b69;
  --navy-strong: #1b2444;
  --navy-wash: #eaeef7;

  --gold: #8a6510;
  --gold-wash: #f7efd6;

  --violet: #5b3fa8;
  --violet-wash: #ece6f8;
  --shot-clip: #2c3b69;
  --shot-photo: #8a6510;
  --shot-lib: #2e7d5b;
  --shot-gen: #5b3fa8;

  /* One colour per specialist agent, taken from the palette above rather than
     invented, so a pitch pill, a mix meter and a format card all agree on what
     Reach looks like. They were four hardcoded hexes repeated in three places,
     and the two blues went unreadable the moment the theme flipped.
     These resolve against whichever --navy/--good/--violet wins on :root, so
     one declaration covers both themes. */
  --intent-reach: var(--navy);
  --intent-reach-wash: var(--navy-wash);
  --intent-craving: var(--brand-strong);
  --intent-craving-wash: var(--brand-wash);
  --intent-trust: var(--good);
  --intent-trust-wash: var(--good-wash);
  --intent-booking_order: var(--violet);
  --intent-booking_order-wash: var(--violet-wash);

  --bg: #f9f7f4;
  --surface: #ffffff;
  --surface-2: #f3f0eb;
  --surface-3: #e8e3da;
  --field: #ffffff;

  --fg: #16203a;
  --fg-2: #4a5468;
  /* The two quiet greys are text, so they clear WCAG AA (4.5:1) on every
     surface they sit on, down to --surface-2. They were 3.5:1 and 2:1, which
     read as decoration to anyone with less than perfect sight -- see rule 14
     in docs/compliance.md. The hierarchy now comes from size and weight as
     much as from fading the colour out. */
  --fg-3: #585f6d;
  --fg-4: #666d7b;

  --line: #e4dfd6;
  --line-soft: #f0ece5;
  --line-strong: #d0c8bc;

  /* Good and warn are text on their own washes as often as they are fills,
     so both are deep enough to clear 4.5:1 there (rule 14). */
  --good: #2a7353;
  --good-wash: #e8f4ee;
  --warn: #935c0f;
  --warn-wash: #fbf1de;
  --bad: #b23b2c;
  --bad-wash: #fbecea;

  --scrim: rgba(16, 24, 40, 0.55);
  --veil: rgba(249, 247, 244, 0.85);

  /* A card is told apart from the page by its border and its surface, not by
     how far it floats. Depth is spent on the things that genuinely sit over
     the page -- a menu, a sheet, a dialog -- and everything resting in the
     flow gets a hairline and almost nothing else. */
  --shadow-1: 0 1px 2px rgba(22, 32, 58, 0.04);
  --shadow-2: 0 1px 2px rgba(22, 32, 58, 0.04), 0 6px 16px -10px rgba(22, 32, 58, 0.1);
  --shadow-3: 0 2px 6px rgba(22, 32, 58, 0.05), 0 20px 40px -16px rgba(22, 32, 58, 0.18);

  /* Runs the mark's own ramp — amber into ember. Every stop clears the 3:1
     contrast floor against the white label, which the amber end did not: it
     started at 2.58:1, and on a btn-sm the label sits right in that corner.
     The floor caps lightness, so the amber came down to meet it with hue and
     saturation held rather than being greyed off. The hover ramp still sits
     above the base at all three stops, so the lift survives the cap. */
  /* A primary button's white label has to clear 4.5:1 at every stop of the
     fill (rule 14). The old ember ran from #e37110, 3.16:1 -- large text only,
     and a button label is 14px. This one starts at 4.57:1. */
  --grad-brand: linear-gradient(135deg, #c7501a 0%, #bd4619 55%, #a83b15 100%);
  --grad-brand-hover: linear-gradient(135deg, #c24c1b 0%, #b8431a 55%, #a33a16 100%);
  --grad-navy: linear-gradient(135deg, #3a4a7d 0%, #2c3b69 55%, #1b2444 100%);
  --grad-good: linear-gradient(135deg, #2f8f6d 0%, #1f6b50 100%);
  --grad-violet: linear-gradient(135deg, #7c5cd6 0%, #5b3fa8 100%);
  --grad-danger: linear-gradient(180deg, #d6503d 0%, #b23b2c 100%);
  --grad-danger-hover: linear-gradient(180deg, #e05a47 0%, #c04434 100%);
}

[data-theme="dark"] {
  --brand: #f5883a;
  --brand-strong: #fba355;
  --brand-wash: rgba(245, 136, 58, 0.16);
  --brand-ink: #f5883a;
  --on-brand: #101828;

  --navy: #8b99cd;
  --navy-strong: #a3aeda;
  --navy-wash: rgba(139, 153, 205, 0.16);

  --gold: #ddb65a;
  --gold-wash: rgba(221, 182, 90, 0.14);

  --violet: #b09ae8;
  --violet-wash: #2b2340;
  --shot-clip: #8b99cd;
  --shot-photo: #ddb65a;
  --shot-lib: #55b98e;
  --shot-gen: #b09ae8;

  --bg: #12151f;
  --surface: #191d2a;
  --surface-2: #212635;
  --surface-3: #2d3346;
  --field: #141824;

  --fg: #edeff4;
  --fg-2: #a8afc0;
  --fg-3: #939bad;
  --fg-4: #868ea1;

  --line: #2d3346;
  --line-soft: #212635;
  --line-strong: #3e4560;

  --good: #55b98e;
  --good-wash: rgba(85, 185, 142, 0.14);
  --warn: #d3993f;
  --warn-wash: rgba(211, 153, 63, 0.14);
  --bad: #e0705f;
  --bad-wash: rgba(224, 112, 95, 0.14);

  --scrim: rgba(0, 0, 0, 0.7);
  --veil: rgba(18, 21, 31, 0.85);

  --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.3);
  --shadow-2: 0 1px 2px rgba(0, 0, 0, 0.25), 0 6px 16px -10px rgba(0, 0, 0, 0.5);
  --shadow-3: 0 2px 6px rgba(0, 0, 0, 0.3), 0 20px 40px -16px rgba(0, 0, 0, 0.65);

  --grad-brand: linear-gradient(135deg, #f79a45 0%, #ee7430 55%, #e05f24 100%);
  --grad-brand-hover: linear-gradient(135deg, #fca752 0%, #f5823a 55%, #ea6c2b 100%);
  --grad-navy: linear-gradient(135deg, #4a5a92 0%, #3a4a7d 55%, #2c3b69 100%);
  --grad-good: linear-gradient(135deg, #3aa47f 0%, #27805f 100%);
  --grad-violet: linear-gradient(135deg, #8f70e4 0%, #6b4dbd 100%);
  --grad-danger: linear-gradient(180deg, #e0705f 0%, #c65545 100%);
  --grad-danger-hover: linear-gradient(180deg, #e88070 0%, #d06050 100%);
}

/* ---------- Type & metrics ---------- */
/* Text sizes come from one scale, a pixel apart at the small end:
   10 11 12 13 14 15 16 17 18 20 22 24 26px, written in rem
   (0.625 0.6875 0.75 0.8125 0.875 0.9375 1 1.0625 1.125 1.25 1.375 1.5
   1.625). Sizes like 0.82rem and 0.84rem sat side by side as near-twins,
   about fifty of them in the portal alone, and a page read as uneven without
   any one size being wrong. Display headings above 26px are set one by one.
   tests/type_scale_check.js holds the product stylesheets to it. */

:root {
  --display: "Fraunces", "Iowan Old Style", Georgia, serif;
  --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Aliases for names used around the app that predate the numbered scale.
     Written as var() so each still resolves per-theme at the point of use. */
  --fg-1: var(--fg);
  --surface-1: var(--surface);
  --shadow-sm: var(--shadow-1);
  --shadow-md: var(--shadow-2);

  /* Softer than a web app's usual 4/8/12. The corner is the loudest thing a
     flat interface says about itself, and at 8px a button reads as a form
     control on a page; at 10-14 it reads as a surface you can press. Every
     primitive here -- button, field, card, pill -- takes its corner from this
     scale, so the whole portal moves together rather than one screen at a
     time. Literal radii elsewhere (a 4px thumbnail, a 6px icon button) are
     deliberately below the scale and stay where they are. */
  --r-xs: 6px;
  --r-sm: 10px;
  --r-md: 14px;
  --r-lg: 20px;
  --r-xl: 28px;
  --r-pill: 999px;

  --ease: cubic-bezier(0.2, 0.8, 0.2, 1);
}

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* A class that sets `display` otherwise beats the attribute, leaving elements
   marked hidden still painted — spinners being the obvious casualty. */
[hidden] {
  display: none !important;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.55;
  /* Inter sets a touch wide at UI sizes. The headings already pull in; the
     body reading loose against them was the tell. */
  letter-spacing: -0.01em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -webkit-tap-highlight-color: transparent;
}

h1,
h2,
h3 {
  font-family: var(--display);
  font-weight: 600;
  font-variation-settings: "SOFT" 4, "WONK" 0;
  letter-spacing: -0.012em;
  line-height: 1.12;
}

h4,
h5,
h6 {
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.3;
}

a {
  color: inherit;
}

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

button,
input,
select,
textarea {
  font: inherit;
  color: inherit;
}

::selection {
  background: var(--brand);
  color: var(--on-brand);
}

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

/* Small-caps label used above section headings and over form groups. */
.eyebrow {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--fg-3);
}

/* ---------- Logo lockup ---------- */

.kd-logo {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  text-decoration: none;
  color: var(--fg);
}

/* The flame is a tall, narrow silhouette (roughly 4:5), so a square box paints
   it far narrower than the box implies — at 1.45em it inked only ~18px wide
   beside a ~104px wordmark and read as an afterthought. Sized off the mark's
   height instead, with width left to the aspect ratio so no side padding is
   reintroduced. */
.kd-logo img {
  height: 2em;
  width: auto;
  flex: none;
}

.kd-logo b {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.06em;
  letter-spacing: -0.025em;
  white-space: nowrap;
}

/* ---------- Buttons ---------- */

.btn {
  --btn-bg: var(--surface);
  --btn-fg: var(--fg);
  --btn-line: var(--line-strong);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  padding: 0.62em 1.05em;
  border: 1px solid var(--btn-line);
  border-radius: var(--r-sm);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: -0.005em;
  text-decoration: none;
  /* A label that breaks mid-button reads as a rendering fault. Nowrap also
     puts a floor under the button's min-content width, so a flex row shrinks
     the prose beside it rather than squeezing the action. */
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 0.15s var(--ease), border-color 0.15s var(--ease),
    transform 0.08s var(--ease), color 0.15s var(--ease);
}

.btn:hover {
  background: var(--surface-2);
}

.btn:active {
  transform: translateY(1px);
}

/* Disabled has to read as unavailable at a glance. Fading a gradient to 55%
   leaves a button that still looks painted and pressable — people click it and
   then wonder why nothing happened — so the treatment strips the paint instead
   of thinning it: flat light surface, muted label, no shadow, no hover, no
   press. Applies to every button in the app, not one form's save. */
.btn:disabled,
.btn[disabled],
.btn[aria-disabled="true"] {
  background: var(--surface-2);
  background-image: none;
  border-color: var(--line);
  color: var(--fg-4);
  box-shadow: none;
  text-shadow: none;
  opacity: 1;
  cursor: not-allowed;
  transform: none;
}

.btn:disabled:hover,
.btn[disabled]:hover,
.btn[aria-disabled="true"]:hover {
  background: var(--surface-2);
  border-color: var(--line);
}

.btn svg {
  flex: none;
}

/* The ramp stays -- it is the mark's own amber-into-ember and it is what makes
   the button read as ours rather than as a default blue. What went is the
   moulding around it: a highlight inset along the top edge, a shadow inset
   along the bottom and a drop shadow under the label, which together drew a
   physical button with a bevel. Nothing else in the portal is lit from above,
   so the one element pretending to be was the one that looked its age. Flat
   paint, one soft brand-tinted shadow to lift it off the page, and the press
   is a press rather than a dent. */
.btn-primary {
  --btn-fg: var(--on-brand);
  --btn-line: transparent;
  background: var(--grad-brand);
  box-shadow: 0 1px 2px rgba(140, 55, 20, 0.16), 0 4px 12px -6px rgba(196, 72, 27, 0.32);
}

.btn-primary:hover {
  background: var(--grad-brand-hover);
  border-color: transparent;
  box-shadow: 0 1px 2px rgba(140, 55, 20, 0.18), 0 6px 16px -6px rgba(196, 72, 27, 0.38);
}

.btn-primary:active {
  background: var(--grad-brand);
  box-shadow: 0 1px 2px rgba(140, 55, 20, 0.16);
}

[data-theme="dark"] .btn-primary {
  --btn-line: transparent;
}



.btn-quiet {
  --btn-line: transparent;
  --btn-bg: transparent;
  color: var(--fg-2);
}

.btn-quiet:hover {
  background: var(--surface-2);
  color: var(--fg);
}

.btn-lg {
  padding: 0.85em 1.4em;
  font-size: 1rem;
}

.btn-block {
  width: 100%;
}

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

.field {
  margin-bottom: 1.1rem;
}

.field label,
.field-label {
  display: block;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--fg-2);
  margin-bottom: 0.35rem;
}

.field .hint {
  display: block;
  font-size: 0.8125rem;
  color: var(--fg-3);
  margin-top: 0.4rem;
  line-height: 1.45;
}

.input {
  width: 100%;
  padding: 0.62em 0.75em;
  background: var(--field);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  /* 16px, and not a sixteenth less. Safari zooms the whole page when a control
     under 16px takes focus, and it does not zoom back out on blur -- so on a
     phone, tapping any field in the portal left the owner scrolled sideways in
     a form they were trying to fill in at the pass. It read as 0.94rem, which
     is 15.04px, and one pixel was the whole of it. The fix is the font size,
     never user-scalable=no, which takes zoom away from people who need it. */
  font-size: 1rem;
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}

.input::placeholder {
  color: var(--fg-4);
}

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

/* A locked field that still looks writable gets typed into. Same reasoning as
   the disabled button above: strip the paint rather than thin it. */
.input:disabled {
  background: var(--surface-2);
  color: var(--fg-4);
  cursor: not-allowed;
}

select.input {
  appearance: none;
  cursor: pointer;
  padding-right: 2.1em;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%2376837a' stroke-width='1.6' stroke-linecap='round'%3E%3Cpath d='M4 6.5 8 10.5 12 6.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.7em center;
  background-size: 15px;
}

textarea.input {
  resize: vertical;
  min-height: 4.5em;
  line-height: 1.5;
}

/* ---------- Toggle ---------- */

.toggle {
  position: relative;
  flex: none;
  width: 40px;
  height: 23px;
}

.toggle input {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}

.toggle span {
  position: absolute;
  inset: 0;
  border-radius: var(--r-pill);
  background: var(--surface-3);
  border: 1px solid var(--line);
  transition: background-color 0.18s var(--ease), border-color 0.18s var(--ease);
  pointer-events: none;
}

.toggle span::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 17px;
  height: 17px;
  border-radius: 50%;
  background: #fff;
  box-shadow: var(--shadow-1);
  transition: transform 0.18s var(--ease);
}

.toggle input:checked + span {
  background: var(--brand);
  border-color: var(--brand);
}

.toggle input:checked + span::after {
  transform: translateX(17px);
}

.toggle input:focus-visible + span {
  box-shadow: 0 0 0 3px var(--brand-wash);
}

/* ---------- Status pill ---------- */

.pill {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  padding: 0.2em 0.55em;
  border-radius: var(--r-xs);
  border: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--fg-2);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  white-space: nowrap;
}

.pill-good {
  background: var(--good-wash);
  border-color: transparent;
  color: var(--good);
}

.pill-warn {
  background: var(--warn-wash);
  border-color: transparent;
  color: var(--warn);
}

.pill-bad {
  background: var(--bad-wash);
  border-color: transparent;
  color: var(--bad);
}

/* ---------- Legal footer ----------
   Rule 1 in docs/compliance.md: the privacy policy is linked from every
   standalone page and from the portal. One quiet line, the same everywhere. */
.legal-foot { margin: 0; font-size: 0.8125rem; line-height: 1.7; color: var(--fg-3); text-align: center; }
.legal-foot a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.legal-foot a:hover { color: var(--fg); }

.pill-brand {
  background: var(--brand-wash);
  border-color: transparent;
  color: var(--brand-ink);
}

.pill-gold {
  background: var(--gold-wash);
  border-color: transparent;
  color: var(--gold);
}

.pill-violet {
  background: var(--violet-wash);
  border-color: transparent;
  color: var(--violet);
}

/* A slow pulse for anything the worker is still chewing on. */
.pill-live::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  animation: kd-pulse 1.6s var(--ease) infinite;
}

@keyframes kd-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.25; }
}

/* ---------- Theme switch ---------- */

.theme-btn {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  flex: none;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--fg-2);
  cursor: pointer;
  transition: background-color 0.15s var(--ease), color 0.15s var(--ease);
}

.theme-btn:hover {
  background: var(--surface-2);
  color: var(--fg);
}

.theme-btn svg {
  width: 16px;
  height: 16px;
}

/* ---------- Touch targets ----------
   The portal is used one-handed standing at the pass, and its small round
   controls are painted between 22 and 34px. The floor for a target is 24px and
   the comfortable size is 44, but these sit in rows with 8px gaps: a plain
   44px square around each would reach 5 to 8px past the paint on both sides
   and start answering for its neighbour, which is its own fault and a worse
   one -- a sign-out that fires when you meant the theme switch.

   The crowding is horizontal and the room is vertical, so the target grows the
   way it can: a finger's height, no wider than the paint except for the one
   control under 24px across. Nothing moves and no two targets meet. The portal
   sheet does the same for its own small controls. */
.icon-btn,
.theme-btn {
  position: relative;
}

.icon-btn::after,
.theme-btn::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: max(100%, 24px);
  height: 44px;
  transform: translate(-50%, -50%);
}

/* ---------- Toasts (replaces alert()) ---------- */

.toast-stack {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  z-index: 300;
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: min(420px, calc(100vw - 32px));
  pointer-events: none;
}

.toast {
  display: flex;
  align-items: flex-start;
  gap: 0.65em;
  padding: 0.75em 0.9em;
  border-radius: var(--r-md);
  background: var(--fg);
  color: var(--bg);
  font-size: 0.875rem;
  line-height: 1.4;
  box-shadow: var(--shadow-3);
  animation: kd-toast-in 0.24s var(--ease);
}

.toast.leaving {
  animation: kd-toast-out 0.18s var(--ease) forwards;
}

.toast svg {
  width: 16px;
  height: 16px;
  margin-top: 2px;
  flex: none;
}


@keyframes kd-toast-in {
  from { opacity: 0; transform: translateY(10px) scale(0.98); }
  to { opacity: 1; transform: none; }
}

@keyframes kd-toast-out {
  to { opacity: 0; transform: translateY(6px); }
}

/* ---------- Dialog ---------- */

.sheet-backdrop {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: var(--scrim);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}

.sheet-backdrop.open {
  display: flex;
  animation: kd-fade 0.16s var(--ease);
}

body.sheet-open {
  overflow: hidden;
}

.sheet {
  width: 100%;
  max-width: 430px;
  max-height: calc(100vh - 32px);
  overflow-y: auto;
  padding: 1.4rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-3);
  animation: kd-sheet-in 0.22s var(--ease);
}

.sheet-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.2rem;
}

.sheet-head h3 {
  font-size: 1.25rem;
}

.sheet-head p {
  font-size: 0.875rem;
  color: var(--fg-3);
  margin-top: 0.2rem;
}

.icon-btn {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  flex: none;
  border: none;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--fg-3);
  cursor: pointer;
  transition: background-color 0.15s var(--ease), color 0.15s var(--ease);
}

.icon-btn:hover {
  background: var(--surface-2);
  color: var(--fg);
}

@keyframes kd-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes kd-sheet-in {
  from { opacity: 0; transform: translateY(12px) scale(0.985); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
