/* Wave 5: the foundation under the foundation.

   Nothing on this sheet changes a layout. It gives the hub a perceptual
   palette (one hue for every grey, so dividers and placeholders read as
   chosen for this brand), a numeric face that actually has tabular figures
   (Quicksand has none, so every tabular-nums the earlier waves set was
   ignored), tinted shadows, glass on the two navigation bars and nowhere
   else, the type details print designers do by hand, one icon family and
   one empty-state component.

   The ramps are the source of truth; hub.css :root re-points its semantic
   tokens (--ink, --line, --ok ...) at them, so every component keeps the
   name it already reads and lands on the new value. The hex block at the
   end is for browsers without oklch(); the values come from the hex columns
   in docs/polish-research-2026-10/02-visual-typography-colour.md section 2
   and the contrast test reads the same hexes. */

/* 1. Ramps. Hue 159 is the forest; chroma rises as lightness falls so the
   lightest tints never look dirty and the darkest inks lean green, not
   black. Success sits at hue 150, one notch off the brand green, so "done"
   is not the same colour as the brand. */
:root {
  --neutral-0: oklch(0.995 0.002 159);
  --neutral-50: oklch(0.975 0.004 159);
  --neutral-100: oklch(0.961 0.007 159);
  --neutral-150: oklch(0.940 0.009 159);
  --neutral-200: oklch(0.905 0.011 159);
  --neutral-300: oklch(0.850 0.013 159);
  --neutral-400: oklch(0.720 0.016 159);
  --neutral-500: oklch(0.600 0.020 159);
  --neutral-600: oklch(0.500 0.025 159);
  --neutral-700: oklch(0.420 0.035 159);
  --neutral-800: oklch(0.330 0.050 159);
  --neutral-900: oklch(0.250 0.040 159);
  --neutral-950: oklch(0.190 0.030 159);

  --forest-50: oklch(0.97 0.02 159);
  --forest-100: oklch(0.94 0.04 159);
  --forest-200: oklch(0.88 0.07 159);
  --forest-300: oklch(0.80 0.10 159);
  --forest-400: oklch(0.68 0.12 159);
  --forest-500: oklch(0.55 0.11 159);
  --forest-600: oklch(0.45 0.09 159);
  --forest-700: oklch(0.38 0.08 159);
  --forest-800: oklch(0.334 0.069 159);
  --forest-900: oklch(0.27 0.055 159);
  --forest-950: oklch(0.21 0.04 159);

  /* Lime 300 to 500 sit outside sRGB at this chroma; the browser gamut-maps
     them and P3 screens get the real thing below. */
  --lime-50: oklch(0.985 0.06 112);
  --lime-100: oklch(0.97 0.11 112);
  --lime-200: oklch(0.945 0.172 112);
  --lime-300: oklch(0.90 0.18 112);
  --lime-400: oklch(0.82 0.18 112);
  --lime-500: oklch(0.72 0.159 112);
  --lime-600: oklch(0.60 0.133 112);
  --lime-700: oklch(0.50 0.11 112);
  --lime-800: oklch(0.40 0.085 112);

  --lilac-50: oklch(0.975 0.013 299);
  --lilac-100: oklch(0.95 0.027 299);
  --lilac-200: oklch(0.90 0.056 299);
  --lilac-300: oklch(0.841 0.085 299);
  --lilac-400: oklch(0.76 0.11 299);
  --lilac-500: oklch(0.64 0.15 299);
  --lilac-600: oklch(0.52 0.17 299);
  --lilac-700: oklch(0.44 0.16 299);
  --lilac-800: oklch(0.36 0.13 299);
  --lilac-900: oklch(0.28 0.10 299);

  /* State. The 600 step is text on cream, the 700 and 800 steps are text on
     lilac, where the canvas is lighter than its hex suggests. */
  --success-600: oklch(0.50 0.125 150);
  --success-700: oklch(0.44 0.121 150);
  --success-800: oklch(0.40 0.111 150);
  --success-tint: oklch(0.96 0.04 150);
  --success-tint-lilac: oklch(0.93 0.04 150);
  --warning-600: oklch(0.52 0.113 70);
  --warning-700: oklch(0.46 0.10 70);
  --warning-800: oklch(0.40 0.087 70);
  --warning-tint: oklch(0.96 0.029 70);
  --warning-tint-lilac: oklch(0.93 0.04 70);
  --danger-600: oklch(0.52 0.16 25);
  --danger-700: oklch(0.44 0.16 25);
  --danger-800: oklch(0.38 0.14 25);
  --danger-tint: oklch(0.96 0.019 25);
  --danger-tint-lilac: oklch(0.93 0.035 25);
  --info-600: oklch(0.52 0.147 250);
  --info-700: oklch(0.44 0.125 250);
  --info-tint: oklch(0.96 0.019 250);
  --info-tint-lilac: oklch(0.93 0.035 250);

  /* The packaging cream stays exact; everything derived from it sits at the
     forest hue. */
  --surface-0: #F4F2ED;

  /* Shadow colour: the forest at seven percent, so a lift reads as ambient
     light on cream rather than as a grey smudge. */
  --shadow-color: oklch(0.33 0.05 159 / .07);

  /* Numeric face. */
  --font-num: "Inter", system-ui, sans-serif;

  /* Utopia steps, fluid between 390px and 1440px, ratio 1.2 to 1.25. For
     headings only: a table cell or a label that changes size with the window
     breaks the alignment that the tabular face just bought. */
  --step-0: clamp(0.94rem, 0.92rem + 0.1vw, 1rem);
  --step-1: clamp(1.13rem, 1.08rem + 0.2vw, 1.25rem);
  --step-2: clamp(1.35rem, 1.26rem + 0.37vw, 1.56rem);
  --step-3: clamp(1.62rem, 1.47rem + 0.62vw, 1.95rem);
  --step-4: clamp(1.94rem, 1.69rem + 1.0vw, 2.44rem);
  --step-5: clamp(2.33rem, 1.92rem + 1.6vw, 3.05rem);
}

/* The creators' phones are P3: the lime gets the chroma the hex cannot hold. */
@media (color-gamut: p3) {
  :root {
    --lime-200: oklch(0.945 0.19 112);
    --lime: oklch(0.945 0.19 112);
  }
}

/* 2. The portal on lilac. Secondary text steps one notch darker and the
   state colours to their 700 and 800 steps, because neutral-600 is 3.5:1 on
   lilac and success-700 is 4.4:1. Shadows take the lilac hue. */
body.portal-page {
  --ink-soft: var(--neutral-700);
  --ink-faint: var(--neutral-600);
  --ok: var(--success-800);
  --ok-ink: var(--success-800);
  --ok-soft: var(--success-tint-lilac);
  --warn: var(--warning-800);
  --warn-ink: var(--warning-800);
  --warn-soft: var(--warning-tint-lilac);
  --bad: var(--danger-700);
  --bad-ink: var(--danger-700);
  --bad-soft: var(--danger-tint-lilac);
  --shadow-color: oklch(0.36 0.13 299 / .08);
  --line: var(--lilac-400);
  --line-soft: var(--lilac-200);
}

/* 3. Numbers. Every selector the earlier waves marked tabular, now in a face
   that carries tnum and zero. Quicksand stays the voice for words; Inter
   never sets a heading or a button. */
.num,
td.num,
th.num,
.stat-value,
.hub-nav-badge,
.needs-age,
.timeline-time,
.funnel-value,
.kpi,
.kpi-value,
.kpi-delta,
.today-number,
.pay-run-list,
.portal-timeline-when,
.pay-list .pay-amount,
.pay-total,
.month-row td .month-total,
.roas-readout,
.consequences .count,
.portal-iban-mask,
.video-tile-duration,
.cut-progress-percent,
.funnel-count,
.funnel-row-days,
.pay-bar-amount,
.rank-value,
.editor-length,
.detail-list dd.num {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums slashed-zero;
  font-optical-sizing: auto;
  font-feature-settings: "cv11" 1;
}

/* The display face set the big stats and KPI digits before. Inter at the
   same size and weight 600 keeps the scale and gains the tabular column;
   Indarung keeps the one title per screen. */
.stat-value,
.kpi-value,
.today-number,
.funnel-count,
.pay-bar-amount,
.consequences .count {
  font-weight: 600;
}

/* A stat whose value is words is not a number. */
.stat-value-words,
.kpi-value.stat-value-words {
  font-family: "Quicksand", sans-serif;
  font-variant-numeric: normal;
}

/* 4. The display face and its fallback. Indarung has 86 glyphs and no
   umlauts, no ß, no ō: a name the cut cannot set falls back to Quicksand at
   weight, in the same slot, rather than to a tofu box. The helper
   display_text decides. */
.display-fallback {
  font-family: "Quicksand", sans-serif;
  font-weight: 700;
}

/* 5. Depth. The shadow stacks in hub.css :root read --shadow-color, so
   every lift takes the canvas hue; only the ring changes here. */
:root {
  --ring: 0 0 0 .5px oklch(0.33 0.05 159 / .18);
}

body.portal-page {
  --ring: 0 0 0 .5px oklch(0.36 0.13 299 / .2);
}

/* 6. Glass, only on the navigation layer. The bars carry no data and the
   page scrolls under them; a 24px fade replaces the hard border, so the bar
   reads as part of the page until something passes beneath it. Nothing else
   in the app gets backdrop-filter (the dialog backdrop's 4px is the modal
   exception, in hub.css). */
@media (max-width: 840px) {
  .top-bar,
  .tab-bar {
    background: color-mix(in oklab, var(--surface-0) 72%, transparent);
    -webkit-backdrop-filter: blur(16px) saturate(1.4);
    backdrop-filter: blur(16px) saturate(1.4);
  }

  .top-bar {
    border-bottom: 0;
  }

  .tab-bar {
    border-top: 0;
  }

  .top-bar::after,
  .tab-bar::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    height: 24px;
    pointer-events: none;
  }

  .top-bar::after {
    top: 100%;
    background: linear-gradient(to bottom, color-mix(in oklab, var(--surface-0) 85%, transparent), transparent);
  }

  .tab-bar::before {
    bottom: 100%;
    background: linear-gradient(to top, color-mix(in oklab, var(--surface-0) 85%, transparent), transparent);
  }

  @media (prefers-reduced-transparency: reduce) {
    .top-bar,
    .tab-bar {
      background: var(--surface-0);
      -webkit-backdrop-filter: none;
      backdrop-filter: none;
    }
  }
}

/* 7. Type details. Balanced headings, tidy paragraph rags, a stable editor,
   hanging quotes where the engine delivers them (Safari), and no synthesised
   small caps anywhere. */
h1, h2, h3 {
  text-wrap: balance;
}

p, li, .toast, .empty-state p, .hint, .field-hint {
  text-wrap: pretty;
}

textarea, [contenteditable] {
  text-wrap: stable;
}

blockquote, .timeline-quote {
  hanging-punctuation: first last;
}

/* Headings on the fluid scale. The hub h1 is body type at weight, so it takes
   step 3; the display titles take step 4. Labels, tables, buttons and nav
   stay at their fixed sizes. */
h1 {
  font-size: var(--step-3);
}

h2 {
  font-size: var(--step-1);
}

h3 {
  font-size: var(--step-0);
}

.portal-hello,
.hub-auth h1 {
  font-size: var(--step-4);
  line-height: 1.05;
}

/* Body: 14px in the hub, 15px on the portal, where she reads at thumb
   distance. */
body.portal-page {
  font-size: 15px;
}

@media (prefers-contrast: more) {
  :root {
    --ink-soft: var(--neutral-800);
    --ink-faint: var(--neutral-700);
    --ink-mute: var(--neutral-600);
    --line: var(--neutral-400);
    --line-soft: var(--neutral-300);
    --line-strong: var(--neutral-600);
  }

  :focus-visible {
    outline-width: 3px;
  }
}

/* 8. Icons. One family (Lucide), round caps to match Quicksand's terminals,
   sized to the type: 20px beside 14px text, 16px in dense rows, 24px in
   empty states and tabs. Never lime. */
.icon {
  display: inline-block;
  flex-shrink: 0;
  vertical-align: middle;
}

.icon-tone {
  opacity: .25;
}

.tab-icon {
  width: 24px;
  height: 24px;
}

.quick-action-icon .icon {
  width: 20px;
  height: 20px;
}

.drop-tile-icon {
  width: 24px;
  height: 24px;
}

/* 9. Empty state: status, one cue, one action; a 40px icon in forest-300
   inside a 72px forest-50 circle. A filtered list takes a neutral icon so
   "nothing matches" does not look like "nothing yet". */
.empty-state {
  padding: 40px 20px;
  text-align: center;
}

.empty-state-icon {
  display: inline-grid;
  place-items: center;
  width: 72px;
  height: 72px;
  margin: 0 auto 14px;
  border-radius: 50%;
  background: var(--forest-50);
  color: var(--forest-300);
}

.empty-state-icon .icon {
  width: 40px;
  height: 40px;
}

.empty-state.is-filtered .empty-state-icon {
  background: var(--neutral-150);
  color: var(--neutral-500);
}

.empty-state-text {
  max-width: 240px;
  margin: 0 auto;
}

.empty-state-status {
  margin: 0;
  font-weight: 700;
  color: var(--ink);
}

.empty-state-cue {
  margin: 4px 0 0;
  color: var(--ink-soft);
}

.empty-state-action {
  margin-top: 16px;
  display: flex;
  justify-content: center;
  gap: 10px;
  flex-wrap: wrap;
}

/* 10. The portal canvas, flat. The lilac is the brand colour only while it
   stays a plain sheet: no gradient, no glow, cream cards and forest ink. */
body.portal-page {
  background: #D2BFFB;
}

.portal-page .card {
  background: var(--neutral-0);
}

/* 11. Hex fallbacks for engines without oklch() (Safari 15.3 and older,
   Chrome 110 and older). Same values as the report's hex columns. */
@supports not (color: oklch(0 0 0)) {
  :root {
    --neutral-0: #FCFEFD;
    --neutral-50: #F5F8F6;
    --neutral-100: #EEF4F0;
    --neutral-150: #E6EDE9;
    --neutral-200: #DAE2DD;
    --neutral-300: #C7D0CB;
    --neutral-400: #9DA8A1;
    --neutral-500: #77847C;
    --neutral-600: #58685E;
    --neutral-700: #3D5346;
    --neutral-800: #1C3D2C;
    --neutral-900: #0E271B;
    --neutral-950: #07180F;
    --forest-50: #EAFAF0;
    --forest-100: #D6F4E2;
    --forest-200: #B1E6C7;
    --forest-300: #83D2A6;
    --forest-400: #4BAF7D;
    --forest-500: #27855A;
    --forest-600: #1C6443;
    --forest-700: #0E4F32;
    --forest-800: #0C4129;
    --forest-900: #072E1C;
    --forest-950: #051E11;
    --lime-50: #FAFFD1;
    --lime-100: #F6FDA5;
    --lime-200: #F0F85F;
    --lime-300: #E2E941;
    --lime-400: #C8CE0B;
    --lime-500: #A8AD02;
    --lime-600: #838700;
    --lime-700: #656902;
    --lime-800: #494C06;
    --lilac-50: #F8F5FF;
    --lilac-100: #F1EBFF;
    --lilac-200: #E3D7FF;
    --lilac-300: #D2BFFB;
    --lilac-400: #BBA1ED;
    --lilac-500: #9A75DA;
    --lilac-600: #784BBB;
    --lilac-700: #61359C;
    --lilac-800: #482776;
    --lilac-900: #311952;
    --success-600: #1B763A;
    --success-700: #00642B;
    --success-800: #005724;
    --success-tint: #E0FAE4;
    --success-tint-lilac: #D6F0DA;
    --warning-600: #915B00;
    --warning-700: #7B4C00;
    --warning-800: #653E00;
    --warning-tint: #FFEFDD;
    --warning-tint-lilac: #FAE4CC;
    --danger-600: #B33736;
    --danger-700: #971A20;
    --danger-800: #7C1117;
    --danger-tint: #FFEDEB;
    --danger-tint-lilac: #FFDFDC;
    --info-600: #006BB9;
    --info-700: #005494;
    --info-tint: #E9F3FF;
    --info-tint-lilac: #D7EAFF;
    --shadow-color: rgba(28, 61, 44, .07);
    --ring: 0 0 0 .5px rgba(28, 61, 44, .18);
  }

  body.portal-page {
    --shadow-color: rgba(72, 39, 118, .08);
    --ring: 0 0 0 .5px rgba(72, 39, 118, .2);
  }

  .top-bar,
  .tab-bar {
    background: rgba(244, 242, 237, .86);
  }
}
