/* ─────────────────────────────────────────────────────────────
   Cotrugli — Colors & Type (design tokens)
   One brand. Two themes. Components reference tokens, never raw hex.
   Dark is default. Light is the inverted alternative.

   THE one token sheet: every surface (the gate at /, auth, marketing,
   the console) loads this file through the ds_root layout. There is no
   second copy anywhere; if a value changes, it changes here.
   @font-face paths point at /fonts/ (served from priv/static/fonts by
   the Phoenix endpoint).
   ───────────────────────────────────────────────────────────── */

/* ─── Brand fonts — variable, self-hosted ─── */
@font-face {
  font-family: "Inter Tight";
  src: url("/fonts/InterTight-VariableFont_wght-3cdfc5dab0621f61ef1f9ace2ac1a3a9.ttf?vsn=d") format("truetype-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Inter Tight";
  src: url("/fonts/InterTight-Italic-VariableFont_wght-b943a16806dfc4772ed712a38b79a4a6.ttf?vsn=d") format("truetype-variations");
  font-weight: 100 900;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("/fonts/JetBrainsMono-VariableFont_wght-514bb1da4b5d654b134572435ffcfea7.ttf?vsn=d") format("truetype-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("/fonts/JetBrainsMono-Italic-VariableFont_wght-85cc70f51b722cc2618ab156b5bd5e5d.ttf?vsn=d") format("truetype-variations");
  font-weight: 100 900;
  font-style: italic;
  font-display: swap;
}

/* .light .mkt-invert: the marketing inversion band. A .mkt-invert subtree
   carries the OPPOSITE theme's tokens; custom properties set directly on
   the band element beat the ones inherited from <html>, so the flip is
   total. Selector lists only — the values are the one palette, unchanged. */
:root,
.light .mkt-invert {
  /* ── Type families ── */
  --font-sans: "Inter Tight", "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  /* ── Type roles (font shorthand: weight size/line-height family) ── */
  --t-display:    500 48px/1.05 var(--font-sans);  /* tracking -0.03em  */
  --t-h1:         500 32px/1.10 var(--font-sans);  /* tracking -0.025em */
  --t-h2:         500 22px/1.20 var(--font-sans);  /* tracking -0.02em  */
  --t-h3:         500 18px/1.30 var(--font-sans);  /* tracking -0.015em */
  --t-body:       400 15px/1.55 var(--font-sans);
  --t-body-sm:    400 13px/1.50 var(--font-sans);
  /* One step up from body, for prose that is SPOKEN rather than tabulated:
     the room's bubbles. 15px is the register's size - dense, scannable, a
     column of rows - and a conversation set in it reads as a log of one. */
  --t-body-lg:    400 16px/1.55 var(--font-sans);
  --t-mono-data:  500 13px/1.50 var(--font-mono);
  --t-mono-label: 500 10px/1.40 var(--font-mono);  /* 0.12em UPPER     */
  --t-micro:      400 11px/1.40 var(--font-sans);
  /* The composer that IS the page. On Practice's front door the writing box
     is not a control sitting on a surface, it is the surface's whole purpose
     - the greeting says what is waiting, the desk says it in a sentence, and
     then there is nothing to do but write. So it takes a size of its own:
     one step above the room's spoken prose, at body's weight rather than a
     heading's, because it is a place to write and not a thing to read. This
     role belongs to that box and to nothing else. */
  --t-compose:    400 18px/1.55 var(--font-sans);
  /* The touch body role. DESIGN.md has documented body-touch since the scale
     was written; it never had a token. 16px is not a taste here, it is the
     floor under which iOS scales the page on focus - and this shell cannot
     pan back - so every field takes it on a coarse pointer. */
  --t-body-touch: 400 16px/1.5 var(--font-sans);

  /* ── Tracking helpers (apply alongside the role) ── */
  --tr-display: -0.03em;
  --tr-h1:      -0.025em;
  --tr-h2:      -0.02em;
  --tr-h3:      -0.015em;
  --tr-label:    0.12em;

  /* ── Spacing scale (4px base) ── */
  --sp-1:  4px;
  --sp-2:  8px;
  --sp-3:  12px;
  --sp-4:  16px;
  --sp-5:  20px;
  --sp-6:  24px;
  --sp-8:  32px;
  --sp-10: 40px;
  --sp-12: 48px;
  --sp-16: 64px;
  --sp-20: 80px;

  /* ── Corner radius (tight — system feeling) ── */
  --r-1: 2px;
  --r-2: 4px;
  --r-3: 6px;
  --r-4: 8px;

  /* ── Layout ── */
  --topbar-h: 56px;   /* the console topbar; the chat drawer hangs beneath it */
  --control-h: 32px;  /* one height for every chrome control (topbar, filters) */
  --tabbar-h: 56px;   /* the phone's bottom tab bar, above the home indicator */

  /* ── Safe area — the notch, the island, the home indicator ──
     ds_root opts into viewport-fit=cover and the manifest is standalone, so
     these are real on every installed console. Named once here: a component
     that writes env() inline is a component that will miss one of the four. */
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);

  /* ── Touch ──
     44px is the floor, which is what marketing.css's own coarse-pointer
     rules already use; the console shipped 40 and two doctrines in one
     system is one too many. --tap-dense is the ONE exception: a control
     inside a scrolling register row, where 44 would double the row's height.
     Never for a destructive verb. --tap-slop expands an inline word-target
     through an ::after inset without growing its box. */
  --tap-min:   44px;
  --tap-dense: 36px;
  --tap-slop:  10px;   /* 16px of line + 2x10 = the --tap-dense floor */

  /* ── Gutters — two shared left edges ──
     Panels (head rows, table edge cells) sit on one; card interiors on the
     other. A head 2px off its first column is the wobble that reads homemade.
     --pad-view is the page gutter: a token so the phone step moves it once
     instead of every surface overriding it. */
  --pad-panel: 16px;  /* panel heads, register edge cells */
  --pad-card:  20px;  /* card interiors */
  --pad-view:  32px;  /* page gutter: .page-header, .view-body */

  /* ── Tracking (two jobs, two tokens) ── */
  --tr-wide: 0.08em;  /* inline chips/badges; --tr-label (0.12em) is 10px labels */

  /* ── Layer scale — the one z-index vocabulary ──
     In-flow surfaces stack at field/raised; sticky headers hold their rows;
     floating chrome climbs drawer → menu → chrome → backdrop → scrim →
     sidebar. If a layer needs a number outside this scale, the layer is
     wrong, not the scale. */
  --z-field: 0;      /* canvas fields, in-flow base layers */
  --z-raised: 1;     /* raised in-flow elements */
  --z-sticky: 2;     /* sticky table headers */
  --z-meter: 6;      /* in-row meter bars */
  --z-nav: 30;       /* the marketing nav pill */
  --z-drawer: 60;    /* the chat panel drawer */
  --z-menu: 70;      /* menus and popovers (user menu, chat history) */
  --z-tip: 90;       /* tooltips */
  --z-chrome: 100;   /* skip link, flash stack */
  --z-backdrop: 200; /* command palette backdrop */
  --z-scrim: 290;    /* mobile sidebar scrim */
  --z-sidebar: 300;  /* mobile sidebar */

  /* ── Motion ──
     Durations are a ramp by element size; easing is a contract:
     movement (transform, size, position) rides --ease-out; pure linear is
     reserved for opacity/color fades and progress loops. Linear movement is
     the single biggest machine-made tell. */
  --easing: linear;            /* fades and color shifts only */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);  /* movement: enter, press, nudge */
  --t-quick: 120ms;   /* press feedback, micro shifts */
  --t-base:  160ms;   /* hovers, small pops */
  --t-menu:  200ms;   /* dropdowns, popovers */
  --t-panel: 280ms;   /* side panels, large surfaces */
  --t-slow:  240ms;

  /* ─────────── DARK THEME — default ─────────── */
  /* The surface ladder is OKLCH, hue-tinted toward the signal (hue 125 at
     whisper chroma) so the neutrals agree with the accent; depth = one step
     up the ladder + an alpha-white hairline, never a shadow (in-flow).
     Floating chrome is different physics: --surface-overlay + --shadow-overlay. */
  --surface:        oklch(0.12 0.004 125);   /* mono ink — page bg */
  --surface-elev:   oklch(0.175 0.006 125);  /* cards, panels */
  --surface-overlay: oklch(0.215 0.008 125); /* floating chrome only */
  --surface-rule:   rgba(255, 255, 255, 0.07);  /* hairline — works on every step */
  --line-strong:    rgba(255, 255, 255, 0.16);  /* interactive/emphasized edges */

  /* Ledger-paper ruling inks (marketing field bands, the gate's canvas):
     verticals faintest, horizontals a touch more present. The solid ink is
     read by page-js for the canvas field, where alpha lines multiply into
     nothing; warm, on the ladder's hue axis. */
  --field-v:          rgba(255, 255, 255, 0.040);
  --field-h:          rgba(255, 255, 255, 0.065);
  --field-rule-solid: #262724;

  /* State overlays: dark-UI hovers LIGHTEN, never darken. */
  --surface-hover:  rgba(255, 255, 255, 0.035);
  --surface-active: rgba(255, 255, 255, 0.06);

  /* ── The conversation's two sides (I16) ──
     The room is the one product surface where two people take turns, and the
     mockup's whole figure/ground is that the operator's bubble is TINTED and
     every desk's is the raised white. --you-tint is that tint, derived on the
     ladder's own hue axis (125) so it agrees with every neutral around it:
     mockup #EEF0E4 is oklch(0.95 0.016 114), which is this rotated onto the
     axis with a little more chroma, because the console's paper is warmer
     than the mockup's and the tint had to stay distinctly olive on it.
     Dark is its twin one step off the page, not a wash of the same grey.
     --shadow-bubble is the room's own named carve-out from the borders-not-
     boxes rule (DESIGN.md 4): 1px of ambient contact, paper only, so a bubble
     reads as handed over rather than printed on the page. It has no offset
     blur to speak of and it is spent on nothing else. */
  --you-tint:       oklch(0.245 0.03 125);
  --you-rule:       oklch(0.33 0.038 125);
  --shadow-bubble:  none;

  /* Anna's hue (I16). Beno speaks in the signal ink his marks have always
     worn - a mark IS an agent, which is what the Signal Rule reserves the
     colour for - and a second desk answering under the same roof was the same
     mark with a different letter in it. Deep teal-blue: off the neutrals' own
     125 axis on purpose, and not --success (a state), not --info (a notice),
     not --warning. Computed as a letter on --surface-elev, the darkest ground
     a mark sits on: 10.1:1 dark, 5.7:1 light. */
  --desk-people:    oklch(0.79 0.105 218);

  /* Mira's hue (CS3). The third desk needs a third mark, and it is read
     BESIDE Anna's in the same conversation, so it is a full 67 degrees off
     her teal-blue rather than a neighbour of it: indigo, on the same
     lightness axis and the same chroma discipline. Off the neutrals' 125 axis
     like Anna's, and not --success, --info or --warning. Computed as a letter
     on --surface-elev, the darkest ground a mark sits on: 9.6:1 dark,
     6.3:1 light. */
  --desk-seo:       oklch(0.79 0.105 285);

  /* The one sanctioned shadow: floating layers only (palette, menus,
     flashes, tooltips), always paired with a hairline border. */
  --shadow-overlay: 0 1px 1px rgba(0, 0, 0, 0.3),
                    0 8px 24px -6px rgba(0, 0, 0, 0.5),
                    0 24px 48px -12px rgba(0, 0, 0, 0.5),
                    inset 0 1px 0 rgba(255, 255, 255, 0.04);

  /* The ground under a cover: the drawer's scrim and the palette's backdrop
     are the same physics, so they are one token. DESIGN.md has named it
     since the palette shipped; the two overlays each hardcoded their own. */
  --scrim:          rgba(0, 0, 0, 0.5);

  /* Human focus is neutral (chartreuse focus would claim the agent's voice). */
  --focus-ring:     rgba(250, 250, 250, 0.55);

  --text:           #fafaf7;  /* primary text — paper, a whisper warm on the ladder's axis */
  --text-hover:     #e4e4e1;  /* primary-button hover: one step off paper, never a dim */
  /* The muted ramp holds WCAG AA (4.5:1+) for small text on both --surface
     and --surface-elev; hierarchy below --text-muted leans on size and case,
     not on dropping below readable contrast. Same lightness as the old hexes,
     retinted to the ladder's hue. */
  --text-muted:     oklch(0.72 0.006 125);  /* de-emphasized */
  --text-subtle:    oklch(0.63 0.006 125);  /* labels, captions */
  --text-faint:     oklch(0.61 0.006 125);  /* micro hints */

  --signal:         #c4f000;  /* chartreuse — AGENT ONLY */
  --signal-text:    #c4f000;  /* signal as TEXT on a surface (light theme darkens it) */
  --signal-soft:    #d8f566;  /* hover on signal */
  --signal-ink:     #2a3300;  /* text on signal fill */

  --success:        #4ade80;
  --danger:         #f87171;
  --warning:        #fbbf24;
  --info:           #60a5fa;
  /* Warning set as TYPE rather than as a fill, the --signal-text split for
     the same reason: on paper the fill amber reads 4.49:1 on --surface-elev,
     which is a FAIL at every size a status word is set in. On dark the fill
     is already 11.35:1 as text, so the two are one value here. */
  --warning-text:   #fbbf24;
  /* Success set as TYPE, same split, same reason: on paper the fill green
     reads 4.48:1 on --surface-elev, a FAIL at the 10px a badge is set in.
     On dark the fill is already 10.88:1 as text, so the two are one value
     here too. Computed in WarningContrastTest, every badge token, both themes. */
  --success-text:   #4ade80;
}

/* ─────────── LIGHT THEME — inverted ─────────── */
/* .mkt-invert alone (dark theme): the band renders as paper. Inside a
   .light page the dark block's .light .mkt-invert selector outranks this. */
.light,
.mkt-invert {
  --surface:        oklch(0.98 0.002 125);
  /* The paper band step, taken as deep as AA allows: --text-faint (0.54)
     pins the floor at L 0.961 (4.53:1); deeper fails 4.5:1 for faint text
     on elevated cards. Band architecture on paper leans on the hairline
     edges elevated bands carry (marketing.css), not on this step alone. */
  --surface-elev:   oklch(0.961 0.004 125);
  --surface-overlay: #fefefb;  /* warm white, same axis as the ladder */
  --surface-rule:   rgba(0, 0, 0, 0.08);
  --line-strong:    rgba(0, 0, 0, 0.18);

  --field-v:          rgba(0, 0, 0, 0.045);
  --field-h:          rgba(0, 0, 0, 0.070);
  --field-rule-solid: #dcdcd8;

  --surface-hover:  rgba(0, 0, 0, 0.03);
  --surface-active: rgba(0, 0, 0, 0.05);

  --shadow-overlay: 0 1px 1px rgba(0, 0, 0, 0.03),
                    0 8px 16px -4px rgba(0, 0, 0, 0.08),
                    0 24px 32px -8px rgba(0, 0, 0, 0.10);

  /* The conversation's two sides on paper (I16). The tint is the mockup's
     #EEF0E4 on the ladder's own hue axis; the rule is its #E1E4D2, the same
     way. Paper is where the bubble needs the contact shadow: on white, a
     hairline around white is not an object anybody is handing you. */
  --you-tint:       oklch(0.95 0.02 125);
  --you-rule:       oklch(0.905 0.03 125);
  --shadow-bubble:  0 1px 2px rgba(0, 0, 0, 0.045);

  /* Anna's hue on paper: the same hue, taken down until a letter at 11px
     clears AA on the elevated step (5.7:1 there, 6.1:1 on the page). */
  --desk-people:    oklch(0.47 0.105 218);

  /* Mira's hue on paper: the same hue, taken down until a letter at 11px
     clears AA on the elevated step (6.3:1 there, 6.7:1 on the page). */
  --desk-seo:       oklch(0.47 0.105 285);

  /* Paper's cover is lighter: a 50% black over white reads as a bruise. */
  --scrim:          rgba(10, 10, 10, 0.38);

  --focus-ring:     rgba(10, 10, 10, 0.5);

  --text:           #0a0a0a;
  --text-hover:     #27272a;
  --text-muted:     oklch(0.44 0.006 125);
  --text-subtle:    oklch(0.51 0.006 125);
  --text-faint:     oklch(0.54 0.006 125);

  /* signal-deep — darker chartreuse so the signal survives on paper. Ink on
     a signal fill is dark here (paper ink washed out at 2.5:1, failing AA on
     the Approve button); hover steps LIGHTER, mirroring dark theme, so the
     dark ink keeps >= 4.5:1 on both fills (6.38:1 rest, 7.45:1 hover). */
  --signal:         #8aac00;
  --signal-soft:    #96ba05;
  --signal-ink:     #1a2000;
  /* Signal as text needs more depth than the fill: #8aac00 reads 2.5:1 on
     paper; this holds 4.96:1. Fills keep --signal; prose keeps this. */
  --signal-text:    #5d7500;

  --success:        #15803d;
  --danger:         #b91c1c;
  --warning:        #b45309;
  --info:           #1d4ed8;
  /* Warning as text, deepened until it clears AA on the ELEVATED step (a
     status badge paints its own --surface-elev, so that is the pair that
     governs). Hue held exactly: #b45309 is oklch(0.5553 0.1455 49.0) and this
     is the same chroma and hue at L 0.520. Computed, both themes, in
     WarningContrastTest: 5.23:1 on --surface-elev, 5.52:1 on --surface. */
  --warning-text:   #a84800;
  /* Success as text, the same deepening: #15803d is oklch(0.5273 0.1371
     150.07) and reads 4.48:1 on --surface-elev, just under the floor. This is
     the same chroma and hue at L 0.521 - 4.61:1 elevated, 4.87:1 on the page.
     Fills keep --success; a word keeps this. */
  --success-text:   #117e3b;
}

/* ── Breakpoint ladder — THREE steps, max-width, desktop-first ──
   Custom properties do not resolve inside a media query and there is no
   build step, so the ladder is a contract stated here and enforced in
   review, not a token.

     sm   560px   phone portrait. One column, one register per screen.
     md   900px   phone landscape + tablet portrait. The shell's drawer and
                  tab-bar boundary: below md the sidebar is off-canvas.
     lg  1200px   desktop. Sidebar docked, chat panel docked beside content.

   Nothing else. A rule reaching for 820 or 1100 is a rule that has not
   decided which of the three steps it belongs to. */

/* ── The coarse-pointer step ──
   Deliberately not a width query: a 900px phone in landscape needs thumb-
   sized chrome and a 900px laptop window does not. Stepping --control-h
   here lifts every control already reading the token (the icon buttons, the
   theme toggle, the register filters, the onboarding fields) in one move,
   and the type roles step with it so a phone stops speaking in 10px. */
@media (pointer: coarse) {
  :root,
  .light .mkt-invert {
    --t-body:       400 16px/1.55 var(--font-sans);
    --t-body-sm:    400 14px/1.50 var(--font-sans);
    --t-mono-data:  500 14px/1.50 var(--font-mono);
    --t-mono-label: 500 12px/1.40 var(--font-mono);
    --t-micro:      400 13px/1.40 var(--font-sans);

    /* 0.12em is a display-caps value. On 10px it was 1.2px of air between
       letters on the app's DEFAULT label face, which is most of the text on
       a phone: legible as a texture, not as words. Caps still need more than
       zero, so it halves rather than going away. */
    --tr-label: 0.06em;

    --control-h: var(--tap-min);
    --pad-card:  16px;
    --pad-view:  16px;
  }
}
