/* GENERATED by scripts/build-web-theme.mjs from packages/theme. Do not hand edit. */

:root {
  /* THE DISPLAY'S OWN SAFE AREAS, as tokens, defined ONCE here (2026-08-02).
     They resolve to 0px on every device that has none, so a page can spend them unconditionally and a
     desktop browser is unaffected. They live in the token block rather than per page for the reason
     --fd-sans lives here: a page that uses a token the package never defined renders wrong SILENTLY, and
     an undefined var inside a calc drops the whole declaration, which would put a control at top 0.
     THEY ARE NOT PERMISSION TO ASK FOR AN EDGE TO EDGE VIEWPORT. That is a separate, explicit opt in
     (fundioViewportMetaCover) and mobile-viewport.test.mjs still refuses the two apart. The name of the
     meta value is deliberately not written here: this block is checked for it, and a rule's own
     explanation must not be the thing that trips the rule. */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-right: env(safe-area-inset-right, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
  /* Surfaces, deep and quiet */
  --fd-bg-0: #06070d;          /* page */
  --fd-bg-1: #090b14;          /* lifted */
  --fd-panel: #0c0e18;         /* card */
  --fd-panel-2: #11131f;       /* card, raised */
  --fd-panel-3: #161827;       /* inset / inputs */
  /* Ink */
  --fd-ink: #f2f4fb;
  --fd-ink-soft: rgba(228,232,245,0.70);
  --fd-ink-faint: rgba(196,204,226,0.46);
  --fd-ink-ghost: rgba(176,186,214,0.26);
  /* One restrained accent: teal to violet */
  --fd-teal: #1f3f96;
  --fd-violet: #7c83ff;
  /* #navy-accent (2026-08-17): THE ACCENT IS THE BRAND GRADIENT. --fd-grad-navy has been the brand since
     31 July and the interface stayed teal; that gap is what this closes. Every accent value below is a
     point on that one ramp, so there is no second blue to keep in step.
       light text  #1f3f96  the ramp's own 52% stop      9.50:1 on white
       dark  text  #4381e6  the ramp's final vector, extended 5% past its end
     THE EXTENSION IS THE ONE DEVIATION AND IT IS MEASURED, NOT PREFERRED: the ramp's lightest real stop
     #3f7ae0 gives 4.86:1 on the page but 4.46:1 on a card and 4.25:1 on an input, and accent text lives on
     cards and inputs. No stop on the brand ramp can carry dark-scheme text at 4.5:1, because navy is dark
     and the surface behind it is darker. #4381e6 is the FIRST point along the ramp's own final vector that
     clears 4.5:1 on all four dark surfaces (worst case 4.62:1 on panel-3).
     #theme-polish (2026-07-24): ink-safe accents. IDENTICAL to --fd-teal/--fd-violet in dark (so dark is
     byte-unchanged), but darkened in the light block below so accent TEXT reads on white. Use these ONLY for
     text/labels; fills, gradients, glows and borders keep the bright --fd-teal/--fd-violet (and their
     --fd-on-* inks), which is why darkening the base tokens in light was unsafe. */
  --fd-teal-ink: #4381e6;
  --fd-violet-ink: #7c83ff;
  --fd-accent-soft: #a9b0ff;
  /* Hairlines */
  --fd-hair: rgba(150,168,224,0.10);
  --fd-hair-2: rgba(150,168,224,0.16);
  --fd-hair-3: rgba(150,168,224,0.24);
  /* Market movement (calm mint rise, soft coral cool) */
  --fd-rise: #4fe0a6;
  --fd-rise-soft: rgba(79,224,166,0.16);
  --fd-cool: #ff7d8a;
  --fd-cool-soft: rgba(255,125,138,0.16);
  /* Accent tints (soft fills, focus rings, glows) so a page never hardcodes a teal/violet alpha. */
  --fd-teal-soft: rgba(67,129,230,0.16);
  --fd-violet-soft: rgba(124,131,255,0.16);
  /* Warning amber (objection pills, medium severity) + its tint. The one warm accent outside rise/cool. */
  --fd-warn: #f0b968;
  --fd-warn-soft: rgba(240,185,104,0.16);
  /* #cinema-3d (2026-07-27, Sebastian's explicit call): MONEY IS GREEN. The front door's money shot must read as
     money, and money that is not green does not. A deliberate, tokenized break from the teal/violet-only accent
     rule, so the parity guard stays at zero literals. */
  --fd-money: #3ce97e;
  /* #paper-tags (2026-07-28): the front door's readable objection tags are DOM paper matching the film's
     tag stock. Sampled from the film's own lit tag faces (v10 frames at the step dwells: 225-247 per
     channel, average ~rgb(235,235,232)); the token sits within a few points so a DOM tag reads as the
     one in focus, not a different material. Slight translucency is applied at use, never here. */
  --fd-paper: #e9e8e3;
  --fd-sans: "Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
  --fd-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;
  /* #design-converge (2026-07-24): the shared scales the base stylesheet + every converged page builds on.
     Spacing is a 4px rhythm; radii, the touch target (Apple/Material 44px min), and the fluid container max. */
  --fd-space-1: 4px; --fd-space-2: 8px; --fd-space-3: 12px; --fd-space-4: 16px;
  --fd-space-5: 24px; --fd-space-6: 32px; --fd-space-7: 48px; --fd-space-8: 64px;
  --fd-radius-sm: 8px; --fd-radius-md: 12px; --fd-radius-lg: 18px; --fd-radius-full: 999px;
  --fd-touch: 44px;            /* minimum comfortable tap target on touch devices */
  --fd-container: 1200px;      /* fluid content max width */
  /* On-accent foreground inks (text that sits ON a colored button) + the modal scrim, tokenized so the base
     stylesheet carries ZERO hardcoded colors. */
  --fd-on-violet: #ffffff; --fd-on-teal: #ffffff; --fd-on-cool: #1a0708;
  --fd-scrim: rgba(4,6,12,0.66);
  /* #dead-tokens (2026-08-14): --fd-shadow-2 was REFERENCED FOUR TIMES AND DEFINED NOWHERE, so every one of
     those box-shadow declarations was discarded by the browser and the element simply had no shadow. It
     failed silently and looked like a design choice, which is why nobody reported it in months.
     ADDED RATHER THAN MAPPED, because there was no shadow token to map to and a literal would have been the
     fifth hardcoded shadow in this file. It is theme-split on purpose: a shadow that reads correctly on a
     near-black surface is invisible on white, and one tuned for white is a smear on black. */
  --fd-shadow-2: 0 10px 30px rgba(0,0,0,0.45);
  /* #front-door (2026-07-31): THE NAVY GRADIENT. New, and it lives HERE rather than in apps/web, because a
     second copy of a colour is the exact defect this repo spent a day paying for. The front page and the
     cockpit both consume this one declaration. It is the LIGHT half's identity: the wordmark, the composer
     border, the power switch and the greeting all take it, so it is a token, not a page style. */
  --fd-grad-navy: linear-gradient(115deg,#0b1436 0%,#14265e 26%,#1f3f96 52%,#2d5cc4 78%,#3f7ae0 100%);
  /* the three stops any SVG samples, so an icon cannot drift from the gradient it sits on */
  --fd-grad-navy-a: #0b1436; --fd-grad-navy-b: #1f3f96; --fd-grad-navy-c: #3f7ae0;
  /* #brand-mark (2026-07-31): THE MARK AS A MASK, so the notch is genuinely TRANSPARENT rather than painted.
     The same mark sits on the white room and the near-black one, so a notch filled with a background colour
     would be wrong on one of them by construction. A CSS mask keys on ALPHA, so the shape is opaque where the
     mark is and simply absent where the notch is, and whatever is behind shows through.
     The geometry is FD_MARK_PATH below; this token and fundioMarkSvg() are generated from that one string. */
  --fd-mark-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48'%3E%3Cmask id='m'%3E%3Crect width='48' height='48' rx='15' fill='%23fff'/%3E%3Cpolygon points='36,-4 52,12 33,15' fill='%23000'/%3E%3C/mask%3E%3Crect width='48' height='48' rx='15' fill='%23000' mask='url(%23m)'/%3E%3C/svg%3E");
  --fd-text-xs: 11px; --fd-text-sm: 13px; --fd-text-base: 15px; --fd-text-lg: 18px; --fd-text-xl: 24px; --fd-text-2xl: 32px;
  /* #dark-bleed fix (2026-07-15): tell the BROWSER the page is dark, so every native surface follows —
     scrollbar tracks on unstyled panels, select/checkbox/date controls, and the default iframe canvas
     (the dialer softphone panel flashed WHITE on each load without this). Flipped with the theme below. */
  color-scheme: dark;
}
/* WHITE IS THE DEFAULT, BY CONSTRUCTION (2026-08-01, Sebastian's call).
   THE PROBLEM WAS STRUCTURAL, not a missing attribute. These --fd-* tokens ARE the dark values in :root, and
   light used to be applied on top by html[data-theme="light"]. So a page that set no attribute, or whose
   reader script had not run yet, painted DARK. The floor was dark by omission rather than by decision.
   THE FIX IS THE SELECTOR, not the values. html:not([data-theme="dark"]) means "light unless dark is
   explicitly chosen", so a page with no attribute, no script and no stored preference renders WHITE, in pure
   CSS, before any script runs. There is no flash to avoid because there is nothing to wait for.
   THE SPECIFICITY IS UNCHANGED: html[data-theme="light"] and html:not([data-theme="dark"]) compute to the
   same weight, because :not() takes the specificity of its argument. So nothing changed about WHICH rule
   wins, only about WHEN it matches. That is what made an 81 occurrence rewrite safe to do mechanically
   instead of swapping 131 declarations with their partners in six files.
   DARK IS NOT REMOVED, it stops being the default. A person who has chosen dark still gets it: the stored
   preference is read exactly as before and the toggle still writes both values. */
html:not([data-theme="dark"]) {
  --fd-bg-0:#f5f7fc; --fd-bg-1:#eef1f9; --fd-panel:#ffffff; --fd-panel-2:#f6f8fd; --fd-panel-3:#eceffb;
  --fd-ink:#161a2c; --fd-ink-soft:rgba(28,33,58,0.74); --fd-ink-faint:rgba(28,33,58,0.54); --fd-ink-ghost:rgba(28,33,58,0.34);
  --fd-accent-soft:#5860d6;
  /* #theme-polish: darkened accent TEXT inks, readable on the white surfaces (teal ~4.9:1, violet ~4.6:1). */
  /* #navy-accent (2026-08-17): ONLY THE -ink VARIANT FLIPS, and the first attempt got this wrong.
     Making the FILL scheme-dependent broke the invariant theme-tokens.test.mjs guards, and the guard was
     right on the design as well as the rule: ink-on-fill contrast does not depend on the page behind the
     button, so #1f3f96 with white ink is 9.50:1 in BOTH schemes and needs no second value. What genuinely
     inverts is accent TEXT, which sits on the page and therefore cares which scheme it is in. */
  --fd-teal-ink:#1f3f96; --fd-violet-ink:#5860d6;
  --fd-hair:rgba(30,45,100,0.10); --fd-hair-2:rgba(30,45,100,0.16); --fd-hair-3:rgba(30,45,100,0.22);
  --fd-rise:#0f9d6b; --fd-cool:#e0556a;
  /* #light-triggered (2026-07-28): the -soft TINTS were absent from the light block, so every
     status chip composited a dark-tuned wash over white and its text lost contrast. They are
     re-stated here at the light accents so a chip reads on a white surface. */
  --fd-rise-soft:rgba(15,157,107,0.14); --fd-cool-soft:rgba(224,85,106,0.14);
  --fd-teal-soft:rgba(31,63,150,0.13); --fd-violet-soft:rgba(88,96,214,0.13);
  --fd-paper:#e9e8e3;
  --fd-scrim:rgba(20,26,44,0.44);
  /* #dead-tokens: the light twin. Tighter and bluer, because a heavy black shadow on a white panel reads as
     dirt rather than depth. If this block is ever missing a token the dark block has, the element keeps its
     DARK value in light mode, which is the #light-triggered failure from 2026-07-28. */
  --fd-shadow-2:0 8px 24px rgba(30,45,100,0.13);
  /* #dead-tokens (2026-08-14): --fd-money had NO light twin, so a money figure would have kept the dark
     mint on a white panel: 1.60:1, which is not readable, it is barely visible. It is used ZERO times
     today, which is the only reason this is a near miss rather than a bug report, and exactly why it is
     worth fixing now: the first person to reach for it would have shipped an invisible number.
     #087a50 measures 5.37:1 on white and 5.05:1 on the light panel, clearing the 4.5:1 body threshold
     rather than only the 3:1 large-text one, because a figure people read as MONEY should not depend on
     being rendered big. The existing light green (--fd-rise #0f9d6b) was rejected at 3.47:1. */
  --fd-money:#087a50;
  --fd-warn:#b26f1a; --fd-warn-soft:rgba(178,111,26,0.14);
  color-scheme: light;
}
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }


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

/* ===== Fundio base: reset-lite (safe globals only) ===== */
*, *::before, *::after { box-sizing: border-box; }
img, svg, video, canvas { max-width: 100%; height: auto; }   /* never overflow a phone viewport */

/* ===== Layout primitives ===== */
.fd-container { width: 100%; max-width: var(--fd-container); margin-inline: auto; padding-inline: var(--fd-space-4); }
.fd-stack { display: flex; flex-direction: column; gap: var(--fd-space-4); }
.fd-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--fd-space-3); }
.fd-grid { display: grid; gap: var(--fd-space-4); grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
.fd-spread { display: flex; align-items: center; justify-content: space-between; gap: var(--fd-space-3); }

/* ===== Typography ===== */
.fd-h1 { font: 700 var(--fd-text-2xl)/1.15 var(--fd-sans); color: var(--fd-ink); margin: 0; letter-spacing: -0.02em; }
.fd-h2 { font: 700 var(--fd-text-xl)/1.2 var(--fd-sans); color: var(--fd-ink); margin: 0; letter-spacing: -0.01em; }
.fd-h3 { font: 600 var(--fd-text-lg)/1.25 var(--fd-sans); color: var(--fd-ink); margin: 0; }
.fd-text { font: 400 var(--fd-text-base)/1.5 var(--fd-sans); color: var(--fd-ink); }
.fd-muted { color: var(--fd-ink-soft); }
.fd-faint { color: var(--fd-ink-faint); }
.fd-mono { font-family: var(--fd-mono); }

/* ===== Cards ===== */
.fd-card { background: var(--fd-panel); border: 1px solid var(--fd-hair-2); border-radius: var(--fd-radius-lg); padding: var(--fd-space-5); }
.fd-card-2 { background: var(--fd-panel-2); }

/* ===== Buttons (comfortable tap target, token colors) ===== */
.fd-btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--fd-space-2);
  min-height: var(--fd-touch); padding: 0 var(--fd-space-4); border-radius: var(--fd-radius-md);
  border: 1px solid var(--fd-hair-3); background: var(--fd-panel-2); color: var(--fd-ink);
  font: 600 var(--fd-text-sm)/1 var(--fd-sans); cursor: pointer; text-decoration: none; transition: filter .15s, background .15s; }
.fd-btn:hover { filter: brightness(1.08); }
.fd-btn:focus-visible { outline: 2px solid var(--fd-teal); outline-offset: 2px; }
.fd-btn-primary { background: var(--fd-violet); border-color: var(--fd-violet); color: var(--fd-on-violet); }
.fd-btn-accent { background: var(--fd-teal); border-color: var(--fd-teal); color: var(--fd-on-teal); }
.fd-btn-ghost { background: transparent; }
.fd-btn-danger { background: var(--fd-cool); border-color: var(--fd-cool); color: var(--fd-on-cool); }
.fd-btn[disabled] { opacity: .5; cursor: not-allowed; }

/* ===== Inputs ===== */
.fd-field { display: flex; flex-direction: column; gap: var(--fd-space-2); }
.fd-label { font: 600 var(--fd-text-sm)/1 var(--fd-sans); color: var(--fd-ink-soft); }
/* #mobile-16px (2026-07-29): iOS Safari ZOOMS THE PAGE whenever a focused form control is under 16px,
   which made every CRM field jolt the layout on tap (43 of 43 were under it, plus 4 of 4 on signup and
   2 of 2 on login). This is the floor, set on the BARE ELEMENTS at the one shared layer every converged
   page builds on, rather than page by page: the offenders are plain inputs that never took the
   .fd-input class. Scoped to the phone breakpoint so desktop typography is untouched, which is also
   what keeps the desktop rendering byte identical. */
@media (max-width: 768px) {
  input, select, textarea, .fd-input, .fd-select, .fd-textarea { font-size: 16px; }
}
.fd-input, .fd-select, .fd-textarea { width: 100%; min-height: var(--fd-touch); padding: var(--fd-space-2) var(--fd-space-3);
  background: var(--fd-panel-3); border: 1px solid var(--fd-hair-2); border-radius: var(--fd-radius-sm);
  color: var(--fd-ink); font: 400 var(--fd-text-base)/1.4 var(--fd-sans); }
.fd-input:focus, .fd-select:focus, .fd-textarea:focus { outline: none; border-color: var(--fd-teal); }
.fd-textarea { min-height: calc(var(--fd-touch) * 2); resize: vertical; }

/* ===== Chips ===== */
.fd-chip { display: inline-flex; align-items: center; gap: var(--fd-space-1); padding: var(--fd-space-1) var(--fd-space-3);
  border-radius: var(--fd-radius-full); background: var(--fd-panel-3); border: 1px solid var(--fd-hair-2);
  color: var(--fd-ink-soft); font: 600 var(--fd-text-xs)/1.4 var(--fd-sans); white-space: nowrap; }

/* ===== Tables (scroll on a phone via the wrap; optional stack mode) ===== */
.fd-table-wrap { width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }
.fd-table { width: 100%; border-collapse: collapse; font: 400 var(--fd-text-sm)/1.4 var(--fd-sans); color: var(--fd-ink); }
.fd-table th, .fd-table td { text-align: left; padding: var(--fd-space-3); border-bottom: 1px solid var(--fd-hair); }
.fd-table th { color: var(--fd-ink-soft); font-weight: 600; }

/* ===== Modals ===== */
.fd-modal-backdrop { position: fixed; inset: 0; background: var(--fd-scrim); display: flex; align-items: center;
  justify-content: center; padding: var(--fd-space-4); z-index: 1000; }
.fd-modal { width: 100%; max-width: 520px; max-height: 90vh; overflow: auto; background: var(--fd-panel);
  border: 1px solid var(--fd-hair-3); border-radius: var(--fd-radius-lg); padding: var(--fd-space-5); }

/* ===== Responsive helpers ===== */
.fd-hide-mobile { }
.fd-only-mobile { display: none; }

/* ===== Mobile-first breakpoints ===== */
@media (max-width: 768px) {
  .fd-container { padding-inline: var(--fd-space-3); }
  .fd-hide-mobile { display: none !important; }
  .fd-only-mobile { display: revert; }
  .fd-h1 { font-size: var(--fd-text-xl); }
  .fd-h2 { font-size: var(--fd-text-lg); }
  .fd-grid { grid-template-columns: 1fr; }
  /* a nav marked responsive collapses to a column on a phone */
  .fd-nav { flex-direction: column; align-items: stretch; }
  /* opt-in: a table that should STACK into cards on a phone (label via data-label on each td) */
  .fd-table-stack thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .fd-table-stack tr { display: block; margin-bottom: var(--fd-space-3); border: 1px solid var(--fd-hair-2); border-radius: var(--fd-radius-md); padding: var(--fd-space-2); }
  .fd-table-stack td { display: flex; justify-content: space-between; gap: var(--fd-space-4); border: 0; padding: var(--fd-space-2) var(--fd-space-1); }
  .fd-table-stack td::before { content: attr(data-label); color: var(--fd-ink-soft); font-weight: 600; }
}

/* On touch devices, guarantee the comfortable tap target even where a page sets a smaller one. */
@media (hover: none) and (pointer: coarse) {
  .fd-btn, .fd-input, .fd-select { min-height: var(--fd-touch); }
}
