/* ── Zurich Kotak brand typeface ───────────────────────────── */ @font-face { font-family: 'Zurich Sans'; src: url('./assets/fonts/ZurichSans-Light.otf') format('opentype'); font-weight: 300; font-style: normal; font-display: swap; } @font-face { font-family: 'Zurich Sans'; src: url('./assets/fonts/ZurichSans-Regular.otf') format('opentype'); font-weight: 400; font-style: normal; font-display: swap; } @font-face { font-family: 'Zurich Sans'; src: url('./assets/fonts/ZurichSans-Medium.otf') format('opentype'); font-weight: 500; font-style: normal; font-display: swap; } :root { /* Brand palette lifted from zurichkotak.com */ --zk-blue: #2167ae; --zk-blue-dark: #1a5490; --zk-blue-deep: #1a528b; --zk-navy: #23366f; --zk-blue-mid: #5495cf; --zk-blue-light: #91bfe3; --zk-cyan: #c7ebf9; --zk-teal: #0e7c74; --zk-teal-ink: #0b5f59; --zk-teal-tint: #e3f4f2; --zk-teal-line: #a5d8d3; --zk-good: #1f9d63; --zk-ink: #2b2e31; --zk-muted: #5d6162; --zk-grey: #a6adaf; --zk-line: #dde4e3; --zk-line-soft: #eceeef; --zk-tint: #f4f9fc; --zk-tint-blue: #ebf3fb; --zk-white: #fff; --zk-danger: #d32f2f; /* Amber and red accents already used for people / lapsed renewals, named so they stop being magic hex literals scattered across four stylesheets. */ --zk-amber: #b5761f; --zk-amber-ink: #93500f; --zk-amber-tint: #fdf1e7; --zk-amber-line: #f0c69a; --zk-danger-ink: #a3261f; --zk-danger-tint: #fdeceb; --zk-danger-line: #f0c2bd; /* ── Shape, depth, motion ──────────────────────────────────── One scale, used everywhere. The palette above is untouched; every shadow below is the brand navy at low alpha, so depth reads as part of the brand rather than as generic grey haze. */ --r-xs: 6px; --r-sm: 8px; --r-md: 12px; --r-lg: 16px; --r-xl: 20px; --r-pill: 999px; --sh-xs: 0 1px 2px rgba(35, 54, 111, 0.05); --sh-sm: 0 1px 2px rgba(35, 54, 111, 0.05), 0 2px 6px -2px rgba(35, 54, 111, 0.07); --sh-md: 0 1px 2px rgba(35, 54, 111, 0.05), 0 10px 24px -8px rgba(35, 54, 111, 0.14); --sh-lg: 0 2px 6px rgba(35, 54, 111, 0.06), 0 20px 42px -14px rgba(35, 54, 111, 0.22); --ring: 0 0 0 3px rgba(33, 103, 174, 0.16); --ease: cubic-bezier(0.4, 0, 0.2, 1); --t-fast: 0.14s var(--ease); --t: 0.2s var(--ease); --grad-blue: linear-gradient(135deg, var(--zk-blue) 0%, var(--zk-blue-deep) 100%); --grad-blue-hover: linear-gradient(135deg, var(--zk-blue-dark) 0%, var(--zk-navy) 100%); /* ── TYPE SCALE ─────────────────────────────────────────────── The app had FIFTY-FOUR distinct font sizes, and forty of them sat between 0.58rem and 0.95rem — a three-pixel band. The most-used size was 0.78rem, which is 12.5px of body copy. That is not a hierarchy; it is a fog of small grey text, and it is exactly why the screen read as a decade-old enterprise tool. Nothing could be emphasised, because everything was already the same size. Nine steps, with real jumps between them. Body sits at 15px, labels at 14, and 12px is the FLOOR — reserved for uppercase eyebrows, never for anything a person has to read. Size does the prioritising so the reader does not have to. */ --fs-3xs: 0.75rem; /* 12px — uppercase eyebrows only */ --fs-2xs: 0.8125rem; /* 13px — timestamps, meta */ --fs-xs: 0.875rem; /* 14px — field labels */ --fs-sm: 0.9375rem; /* 15px — body, field values */ --fs-md: 1rem; /* 16px — emphasis */ --fs-lg: 1.125rem; /* 18px — panel titles */ --fs-xl: 1.375rem; /* 22px — section headings */ --fs-2xl: 1.75rem; /* 28px — page title */ --fs-3xl: 2.25rem; /* 36px — the one number that matters */ --lh-tight: 1.25; --lh-snug: 1.4; --lh-normal: 1.55; --fw-normal: 400; --fw-medium: 500; --fw-semi: 600; /* ── SPACING ────────────────────────────────────────────────── One rhythm. Panels breathe at 24-28px rather than 14-18px, which is the other half of why this felt cramped. */ --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 20px; --sp-6: 24px; --sp-7: 32px; --sp-8: 40px; --sp-9: 56px; /* Display and body are the SAME brand face — Zurich Sans has no bold, so headings and figures lean on weight 500 and tighter tracking, set per use, not on a second family. */ --font-display: 'Zurich Sans', Arial, Helvetica, sans-serif; --font-zurich: 'Zurich Sans', Arial, Helvetica, sans-serif; --font-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace; font-family: var(--font-zurich); /* The root MUST stay an absolute size — every --fs-* token is rem, so sizing the root from one of them is circular. */ font-size: 16px; line-height: var(--lh-normal); color: var(--zk-ink); /* ONE CANVAS. The app used to be white cards floating on a grey-blue ground — the generic admin-template look. The content ground is white now; the sidebar is the only tinted surface, and sections are separated by hairlines and space rather than boxed in shadowed cards. */ background: var(--zk-white); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-rendering: optimizeLegibility; } * { box-sizing: border-box; } html, body, #root { height: 100%; } body { margin: 0; background: var(--zk-white); } button, input, select, textarea { font: inherit; color: inherit; } /* One focus treatment for the whole console: a soft brand ring, never the browser's default outline halfway through a card's rounded corner. */ :focus-visible { outline: 2px solid var(--zk-blue); outline-offset: 2px; } ::selection { background: var(--zk-tint-blue); color: var(--zk-blue-dark); } /* Scrollbars, toned to the palette. Chrome/Safari take the ::-webkit rules, Firefox the standard properties. */ * { scrollbar-width: thin; scrollbar-color: var(--zk-line) transparent; } ::-webkit-scrollbar { width: 10px; height: 10px; } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { border: 3px solid transparent; border-radius: var(--r-pill); background-clip: content-box; background-color: var(--zk-line); } ::-webkit-scrollbar-thumb:hover { background-color: var(--zk-grey); } @keyframes zk-rise { /* Opacity ONLY. A translateY here promoted every element it touched to a GPU layer, and text composited on a transformed layer renders blurry in Chrome on Linux — which made the whole app look soft. The fade alone gives the entrance without ever moving text onto a fractional-pixel layer. */ from { opacity: 0; } to { opacity: 1; } } @keyframes zk-shimmer { from { background-position: -420px 0; } to { background-position: 420px 0; } } /* Motion is decoration here — every animation is an entrance or a hover, so dropping them costs nothing. */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; scroll-behavior: auto !important; } }