/* ═══════════════════════════════════════════════════════════════
   LaikaSuite — Design tokens
   Constructivist plates, lensed fills.

   This file is the single source of truth for colour, type, space,
   radius, elevation and motion across every LaikaSuite application.
   Applications never hard-code a raw value; they consume a token.

   Layering model
     1. Primitives   raw scales, theme independent  (--ls-blue-500)
     2. Semantics    role based, theme aware        (--surface, --text)
     3. Product      per-application accent         (--accent, --accent-soft)

   Note on identity: LaikaSuite *is* astralaika.com, carrying different
   contents. The paper, the ink, the cosmic red, the gold, the rule
   weights, the hard offset shadows, the square corners, the notch, Anton,
   PT Serif, PT Mono and the easing are all the company's, by value.

   Exactly two things are the suite's own, and they are the only two:
   the fifteen application marks, and the material. Where the company
   prints a solid fill, the suite lenses one — a translucent pane that
   blurs the field behind it, lifts its saturation through the blur,
   pushes the result away from the middle of the ramp, and carries a
   bright top edge over a shaded underside. Brutalist construction,
   lensed surfaces. See docs/BRAND.md.
   ═══════════════════════════════════════════════════════════════ */

/* ── Typeface: PT Mono (SIL OFL, self-hosted, no third-party request) ──
   Used only for numeric, tabular and code contexts. Interface type uses
   the platform UI stack so the suite feels native and loads instantly. */
@font-face {
  font-family: "LS Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("./fonts/ptmono-400-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "LS Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("./fonts/ptmono-400-cyrillic.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

/* PT Serif (SIL OFL) — the document serif offered in Write and Slides. */
@font-face {
  font-family: "LS Serif";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("./fonts/ptserif-400-latin.woff2") format("woff2");
}
@font-face {
  font-family: "LS Serif";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("./fonts/ptserif-400i-latin.woff2") format("woff2");
}
@font-face {
  font-family: "LS Serif";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("./fonts/ptserif-700-latin.woff2") format("woff2");
}

/* Anton (SIL OFL) — a single-weight display face for slide titles. */
@font-face {
  font-family: "LS Display";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("./fonts/anton-400-latin.woff2") format("woff2");
}

:root {
  /* ══ 1. Primitives ═══════════════════════════════════════════ */

  /* Neutral — a cool, slightly blue slate. Reads as glass, not as grey. */
  --ls-slate-0:   #ffffff;
  --ls-slate-25:  #fdfaf8;
  --ls-slate-50:  #f7f3ef;
  --ls-slate-100: #efe8e2;
  --ls-slate-200: #e2d8d0;
  --ls-slate-300: #c8bab0;
  --ls-slate-400: #a4958c;
  --ls-slate-500: #7c6e67;
  --ls-slate-600: #5c5049;
  --ls-slate-700: #443b36;
  --ls-slate-800: #2c2521;
  --ls-slate-900: #1c1613;
  --ls-slate-950: #16110f;
  --ls-slate-1000:#0b0807;

  /* Product hues — one harmonic family, each application owns one.
     Chosen for equal perceived weight so no app looks louder. */
  --ls-azure-400:  #7d9bff;
  --ls-azure-500:  #4b6fff;
  --ls-azure-600:  #3554e6;

  /* Khaki, not lime: a muted olive reads as data rather than highlighter,
     and sits better against the warm field than a saturated green. */
  --ls-khaki-400:  #b3bf6a;
  --ls-khaki-500:  #8a9a3d;
  --ls-khaki-600:  #6b7a2c;

  /* Slides is red. To keep it legible as its own product, the suite mark
     gave up colour entirely and went graphite — the parent brand reads as
     the neutral one, the five products carry the hues. */
  --ls-red-400: #f5766e;
  --ls-red-500: #e23b33;
  --ls-red-600: #b92b25;

  --ls-aqua-400:   #38d3ec;
  --ls-aqua-500:   #00b8d9;
  --ls-aqua-600:   #0096b3;

  /* Purple, not blue-violet: it reads warmer beside the ember field and
     puts more distance between Notes and Write's azure. */
  /* Vector — gold. Warm enough to belong to the suite's field, and far
     enough from Sheets' khaki that the two tiles never trade places. */
  /* Video — indigo. Deep enough that it never trades places with
     Write's cornflower azure or Notes' magenta-violet at tile size. */
  /* ── Neutrals ───────────────────────────────────────────────
     Astralaika's own paper, extended into a full ramp.

     The charter originally kept LaikaSuite's surfaces away from the
     company site's, so the suite would have an identity of its own. That
     is reversed deliberately: the four anchor values below are taken
     verbatim from astralaika.com — its card, its paper, its faded rule
     and its ink — and the rest of the ramp is interpolated between them.
     The suite and the company now sit on literally the same beige, which
     is what makes the "by Astralaika" mark read as ownership rather than
     as a badge stuck on afterwards.

     One ramp serves both themes: light takes the near-white end, dark
     takes the deep beige end. Neither is white and neither is charcoal,
     so the liquid-crystal glass has something warm to refract at both
     ends of the day. */
  --ls-sand-0:   #fbf7ec;   /* astralaika.com --card  */
  --ls-sand-50:  #f3ede0;   /* astralaika.com --paper */
  --ls-sand-100: #eae2cf;   /* astralaika.com --paper-2 */
  --ls-sand-200: #dcd2bb;
  --ls-sand-300: #b4aa93;   /* astralaika.com --faded */
  --ls-sand-400: #9a8f78;
  --ls-sand-500: #7b7160;
  --ls-sand-600: #57503f;   /* astralaika.com --ink-soft */
  --ls-sand-700: #423c2f;
  --ls-sand-800: #2e2a21;
  --ls-sand-900: #221f19;
  --ls-sand-950: #16130e;   /* astralaika.com --ink */

  /* Vault — coral. The one application that is not a document,
     and the one that shares the suite mark's own warmth: it is where
     everything else ends up, so it belongs to the parent's family
     rather than taking a place on the products' wheel. Far enough from
     Vector's yellow-gold and Slides' scarlet to be told apart at 16px. */
  --ls-coral-400: #ffa98c;
  --ls-coral-500: #f2795e;
  --ls-coral-600: #c4523a;

  --ls-indigo-400: #8a7cf0;
  --ls-indigo-500: #5a46db;
  --ls-indigo-600: #3e2fa8;

  --ls-gold-400: #f0c24a;
  --ls-gold-500: #d9a017;
  --ls-gold-600: #b27400;
  /* The company's one gold accent only ever appears on ink, where
     --ls-gold-500 measures 7.93:1. The suite's dark theme inverts those
     plates to paper, and no colour that still reads as gold clears AA on
     cream — so the ramp gains one step below its darkest, tuned to the
     same 7.4:1 the light theme's gold buys on ink. */
  --ls-gold-700: #6f4a00;

  --ls-emerald-400: #4cc99a;
  --ls-emerald-500: #109e6a;
  --ls-emerald-600: #0b7c53;

  --ls-violet-400: #b478d6;
  --ls-violet-500: #8b3fa8;
  --ls-violet-600: #6d2c85;

  /* PDF — deep teal. Sits between Calendar's emerald and Whiteboard's
     aqua, and is darker than both, which is what keeps three cool hues
     tellable apart on a 16px launcher tile. */
  --ls-teal-400: #3eaea6;
  --ls-teal-500: #0d7c76;
  --ls-teal-600: #075a56;

  /* Toolbox — leaf green. The one wide gap left on the wheel, between
     Sheets' olive khaki and Calendar's jade emerald: saturated where khaki
     is muted, pure where emerald leans blue. */
  --ls-leaf-400: #6bc55a;
  --ls-leaf-500: #3e9c2e;
  --ls-leaf-600: #2a731f;

  /* Photo — fuchsia. The last stretch of the wheel wide enough to hold a
     hue of its own, between Notes' violet and Vault's rose. Named apart
     from the palette's `magenta` swatch on purpose: that one is a colour a
     user picks, this one is a product's identity. */
  --ls-fuchsia-400: #e45fc0;
  --ls-fuchsia-500: #c21f98;
  --ls-fuchsia-600: #971574;

  /* Packets — petrol blue. The cool quarter of the wheel carried a bright
     cyan at 189° and a light cornflower at 228° with nothing between them;
     this sits at 209° and, more usefully, it is dark where both of its
     neighbours are bright. It is the blue of an instrument face, which is
     the register the brand keeps for anything technical. */
  --ls-petrol-400: #5c93d0;
  --ls-petrol-500: #205c93;
  --ls-petrol-600: #143e64;

  /* Student — crimson. The widest gap left on a wheel carrying fourteen
     hues was the 48° between Photo's fuchsia at 315° and Slides' red at 3°;
     this sits at 339°, equidistant from both. It reads as red beside the
     fuchsia and as pink beside the red, which is the only test that
     matters at tile size. Academia's own colour, and deliberately a wine
     rather than a signal — Slides owns the primary red. */
  --ls-crimson-400: #e2557f;
  --ls-crimson-500: #b81e52;
  --ls-crimson-600: #8a1440;

  /* Signal — state feedback, shared by every application. */
  --ls-success: #12a56d;
  --ls-warning: #d99000;
  --ls-danger:  #e5484d;
  --ls-info:    #3554e6;

  /* Two short names that a handful of surfaces reach for directly. They
     were being used before they existed — `outline: 2px solid var(--focus)`
     with no --focus resolves to currentColor, which is why a focused
     Vector coordinate field and a focused PDF tile were ringed in whatever
     ink happened to be nearest. Aliases, declared once, so the name and
     the value can never drift apart. */
  --focus:  var(--accent);
  --danger: var(--ls-danger);

  /* ══ 2. Type ═════════════════════════════════════════════════
     Four registers, four jobs. Nothing borrows another's voice.

       display  Anton — the poster voice. Headlines, wordmarks, the
                launcher hero, empty-state titles. Uppercase, always.
       sans     a clean grotesque. Chrome only: the interface should not
                have opinions while you are working inside it.
       serif    PT Serif, shared verbatim with astralaika.com. Anything
                meant to be *read* rather than operated — empty-state
                prose, About, the launcher's argument.
       mono     PT Mono. The aerospace-instrument register: numbers,
                coordinates, timecodes, formulas, file sizes, and the
                small technical labels that annotate a diagram. */
  --font-display: "LS Display", "Arial Black", "Helvetica Neue", Impact,
    system-ui, sans-serif;
  /* Anton is single-weight; the substitutes named per script below have to
     go heavy to carry the same optical mass. */
  --font-display-weight: 400;
  /* The interface face, and it is a serif. astralaika.com sets everything
     that is not a headline and not a technical label in PT Serif — it has
     no grotesque anywhere on it — and the suite is that site. The token
     keeps its name because fifteen applications spell it; what it names
     has changed. The platform stack stays at the end of the list for the
     scripts the bundled subsets do not cover, and the emoji faces stay
     because an emoji has no serif. */
  --font-sans: "LS Serif", Georgia, "Times New Roman", "Liberation Serif",
    serif, "Apple Color Emoji", "Segoe UI Emoji";
  --font-mono: "LS Mono", ui-monospace, "SF Mono", "Cascadia Mono", Menlo,
    Consolas, "Liberation Mono", monospace;
  /* The reading face. Bundled, so a paragraph looks the same on every
     platform, and identical by value to the one the company site sets its
     body copy in. Also the "serif" entry in the document font picker. */
  --font-serif: "LS Serif", Georgia, "Times New Roman", "Liberation Serif", serif;

  /* Modular scale, 1.125 minor third, rounded to whole pixels. */
  --text-2xs: 0.6875rem; /* 11 */
  --text-xs:  0.75rem;   /* 12 */
  --text-sm:  0.8125rem; /* 13 — default control size */
  /* 16, which is astralaika.com's body size. The suite used to set 14 and
     the difference was visible the moment the two pages were opened side
     by side: the same paragraph, in the same face, a size apart. Controls
     keep --text-sm and --text-xs, so a toolbar does not grow with it. */
  --text-md:  1rem;      /* 16 — default body size */
  --text-lg:  1rem;      /* 16 */
  --text-xl:  1.125rem;  /* 18 */
  --text-2xl: 1.375rem;  /* 22 */
  --text-3xl: 1.75rem;   /* 28 */
  --text-4xl: 2.25rem;   /* 36 */
  --text-5xl: clamp(2.25rem, 1.4rem + 3.6vw, 3.75rem);

  /* Display scale — a separate ramp, because Anton is only worth using at
     sizes where its condensed cut actually reads as a poster. Below about
     20px it is just a bold sans with tight sidebearings. */
  --display-xs: 1.0625rem;                                 /* 17 — wordmarks */
  --display-sm: clamp(1.25rem, 1.05rem + 0.9vw, 1.625rem); /* 20 → 26 */
  --display-md: clamp(1.75rem, 1.25rem + 2.2vw, 2.75rem);  /* 28 → 44 */
  --display-lg: clamp(2.5rem, 1.15rem + 5.8vw, 5rem);      /* 40 → 80 */

  --leading-tight: 1.18;
  --leading-snug:  1.35;
  --leading-body:  1.6;
  --leading-loose: 1.75;

  --weight-regular: 400;
  --weight-medium:  500;
  --weight-semi:    600;
  --weight-bold:    700;

  /* Optical tightening: large type needs negative tracking to stay calm. */
  --tracking-tight: -0.022em;
  --tracking-snug:  -0.012em;
  --tracking-wide:  0.02em;
  /* astralaika's .lang-current and .lang-menu button both track at 0.08em. */
  --tracking-caps:  0.08em;
  /* astralaika's h1/h2/h3: letter-spacing 0.01em, line-height 1.02. */
  --tracking-display: 0.01em;
  --leading-display: 1.02;
  /* astralaika's .mono, verbatim: 12.5px at 0.12em, uppercase. That is the
     one register the company annotates everything in — its navigation, its
     kickers, its card labels, its footer — and it is the suite's too. */
  --text-label: 0.78125rem;   /* 12.5 */
  --tracking-label: 0.12em;
  /* Its .btn: Anton at 14px, tracked 0.06em, uppercase. */
  --text-action: 0.875rem;    /* 14 */
  --tracking-action: 0.06em;

  /* ══ 3. Space — 4px base grid ════════════════════════════════ */
  --space-0:  0;
  --space-1:  0.25rem;  /*  4 */
  --space-2:  0.5rem;   /*  8 */
  --space-3:  0.75rem;  /* 12 */
  --space-4:  1rem;     /* 16 */
  --space-5:  1.25rem;  /* 20 */
  --space-6:  1.5rem;   /* 24 */
  --space-8:  2rem;     /* 32 */
  --space-10: 2.5rem;   /* 40 */
  --space-12: 3rem;     /* 48 */
  --space-16: 4rem;     /* 64 */
  --space-20: 5rem;     /* 80 */

  /* ══ 4. Radius ═══════════════════════════════════════════════
     astralaika.com contains the string "border-radius" exactly zero
     times. Every plate, every button, every menu, every stamp on that site
     is a square-cornered rectangle, and that is not an oversight — it is
     the constructivist half of the identity. The suite is that site, so
     the rectangle scale is zero all the way down.

     The names survive because fifteen applications spell them, and
     because a scale that is zero today is a scale that can be moved in one
     place if it ever needs to stop being zero.

     --radius-full is the exception and it is not a compromise: it is what
     draws a *circle*, and the circle is one of the five recurring forms —
     the orbit, the node, the dot on a switch. astralaika draws circles
     too. It never draws a rounded rectangle. */
  --radius-xs:   0;
  --radius-sm:   0;
  --radius-md:   0;
  --radius-lg:   0;
  --radius-xl:   0;
  --radius-2xl:  0;
  --radius-3xl:  0;
  --radius-full: 999px;

  /* The notch: the corner the company clips off its buttons and its
     telegram plate. Two sizes, exactly the two it uses. */
  --notch:    polygon(0 0, calc(100% - 14px) 0, 100% 14px, 100% 100%, 0 100%);
  --notch-lg: polygon(0 0, calc(100% - 22px) 0, 100% 22px, 100% 100%, 0 100%);

  /* ══ 5. Motion — one easing family, three speeds ═════════════
     --ease-out is astralaika.com's --ease, value for value. */
  --ease-out:   cubic-bezier(0.22, 1, 0.36, 1);
  --ease-inout: cubic-bezier(0.65, 0, 0.35, 1);
  --ease-spring:cubic-bezier(0.34, 1.56, 0.64, 1);

  --duration-instant: 90ms;
  --duration-fast:    150ms;
  --duration-normal:  240ms;
  --duration-slow:    380ms;
  --duration-slower:  620ms;

  --transition-control: color var(--duration-fast) var(--ease-out),
    background-color var(--duration-fast) var(--ease-out),
    border-color var(--duration-fast) var(--ease-out),
    box-shadow var(--duration-fast) var(--ease-out),
    transform var(--duration-fast) var(--ease-out),
    opacity var(--duration-fast) var(--ease-out);

  /* ══ 6. Layout constants ═════════════════════════════════════ */
  /* 64, not 56: the bar now carries the company's six pixels of red and
     three of ink inside its own box, and the content row has to keep the
     height it had. astralaika.com's own bar is 68. */
  --topbar-h:  64px;
  --toolbar-h: 48px;
  --statusbar-h: 30px;
  --rail-w:    268px;
  --measure:   72ch;           /* comfortable reading column */
  --tap-target: 34px;          /* minimum interactive box */

  /* ══ 7. Z-index scale — named so nothing invents a magic number */
  --z-base:     0;
  --z-sticky:   100;
  --z-toolbar:  200;
  --z-dropdown: 400;
  --z-overlay:  600;
  --z-dialog:   700;
  --z-toast:    800;
  --z-tooltip:  900;

  /* ══ 8. Glass geometry — the signature of the system ═════════

     A pane is a *lens*, not a tint. Three things happen at once and a
     surface that ships only some of them reads as plastic:

       blur        the field behind it goes out of focus
       saturate    the colour that survives the blur comes back stronger,
                   which is what makes the warm field visible *through*
                   the pane instead of merely behind it
       brightness  the result is pushed away from the middle of the ramp —
                   up in the light theme, down in the dark one

     They are declared together as one filter so no surface can ship half
     the material by accident. --glass-lift is the only number in the
     material that differs between the themes, and it differs in direction
     only: the two are measured to lift the pane off the field by the same
     amount (1.15:1 light, 1.13:1 dark). */
  --glass-blur:       20px;
  --glass-blur-heavy: 34px;
  --glass-saturate:   180%;
  --glass-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate))
    brightness(var(--glass-lift));
  --glass-filter-heavy: blur(var(--glass-blur-heavy)) saturate(var(--glass-saturate))
    brightness(var(--glass-lift));

  /* ── The field's wash ────────────────────────────────────────
     Everything that sits on the paper *except* the paper itself, and it is
     byte-for-byte the same list in both themes. Only the sheet underneath
     it changes — which is the whole of the rule that the two themes are
     one material read from opposite ends.

     Two layers, and both are astralaika.com's own:

       the stock   the 45° hairline hatch the company prints its catalogue
                   band on. Ink on paper in the light theme, paper on ink
                   in the dark one, at one identical alpha — the only
                   honest way to mirror a texture, since a fibre in stock
                   is subtractive and a fibre on a lit screen is additive.
       the halos   four warm blooms — the company's cosmic red, its gold,
                   the suite's ember and a low copper — in the same four
                   places, at the same four *strengths*.

     Strength, not alpha. The two are the same thing only on a mid-grey
     sheet, and neither of these sheets is one: #C8321E is a dark red, so
     seven and a half percent of it visibly deepens cream and does almost
     nothing to ink, while #C89B3C is a light gold and does the reverse.
     Each halo's alpha is therefore solved per theme for one number — the
     contrast between the halo's centre and the bare sheet — and it is that
     number, printed beside each pair below, which is identical.

     No azure-and-violet wash: that is what every browser design tool now
     looks like, and it is the fastest way to read as a competitor. */
  --field-wash:
    repeating-linear-gradient(45deg,
      transparent 0 30px, var(--field-grain) 30px 31px),
    radial-gradient(1200px 820px at 10% -10%, rgb(200 50 30 / var(--halo-red)) 0%, transparent 60%),
    radial-gradient(1020px 720px at 94% 2%, rgb(200 155 60 / var(--halo-gold)) 0%, transparent 56%),
    radial-gradient(940px 940px at 58% 110%, rgb(224 81 40 / var(--halo-ember)) 0%, transparent 58%),
    radial-gradient(720px 620px at 78% 62%, rgb(178 116 0 / var(--halo-copper)) 0%, transparent 62%);

  /* astralaika.com's hero sunburst, at the company's own angle, origin and
     alpha. The suite draws it in one place only — the hub's masthead
     poster — exactly as the company site does. */
  --field-rays: repeating-conic-gradient(from -12deg at -6% 106%,
    rgb(200 50 30 / 0.055) 0 3.4deg, transparent 3.4deg 8deg);

  /* ══ 9. The recurring forms ══════════════════════════════════
     Five shapes carry the identity across marks, empty states and
     illustration: the orbit (a ring, usually broken), the trajectory
     (an arc with a node at each end), the star (four-pointed — the
     "astra" in the name), the rule (thin technical lines and
     registration marks) and the node (a filled circle where two things
     meet).

     They ship as *masks*, not as images. A mask takes its colour from
     whatever paints behind it, so one shape serves the light theme, the
     dark theme, every product accent and a disabled state without a
     second copy — and nothing here is a third-party asset. The star is
     the company mark's own star, rescaled onto a 104-unit box. */
  --shape-star: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 104 104"><path d="M52 0Q61 43 104 52Q61 61 52 104Q43 61 0 52Q43 43 52 0Z" fill="black"/></svg>');
  /* A ring opened at the top right — the gap the star sits in.
     215 + 74 = 289 = the circumference of r=46, so the dash pattern
     closes exactly once around and the gap lands where it is aimed. */
  --shape-orbit: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="50" cy="50" r="46" fill="none" stroke="black" stroke-width="4" stroke-linecap="round" stroke-dasharray="215 74" stroke-dashoffset="-1"/></svg>');
  /* The whole mark in one shape: the broken orbit with its star sitting in
     the gap. This is the company mark's outer structure, and it is what
     empty states, loading states and section breaks are built on. */
  --shape-halo: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120"><circle cx="60" cy="60" r="50" fill="none" stroke="black" stroke-width="4" stroke-linecap="round" stroke-dasharray="234 80" stroke-dashoffset="-1"/><path d="M52 0Q61 43 104 52Q61 61 52 104Q43 61 0 52Q43 43 52 0Z" fill="black" transform="translate(82.36 11.64) scale(0.25)"/></svg>');
  /* One arc, a node at each end: the trajectory. */
  --shape-trajectory: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 60"><path d="M8 52C28 6 92 6 112 52" fill="none" stroke="black" stroke-width="3" stroke-linecap="round"/><circle cx="8" cy="52" r="6.5" fill="black"/><circle cx="112" cy="52" r="6.5" fill="black"/></svg>');

  /* Rule weights, and they are the company's three. 1px is the hairline
     inside a menu; 2px is what every plate, button and control is drawn
     in; 3px is what closes a section or a masthead. */
  --rule: 1px;
  --rule-strong: 2px;
  --rule-section: 3px;
  /* The red band across the top of the masthead, and the red spine down
     the side of a card. Both are the company's, both to the pixel. */
  --band: 6px;
  --spine: 8px;
  /* How far a registration tick overshoots the rule it marks. */
  --tick: 4px;

  /* Which way a hard offset shadow falls. The company site is
     left-to-right only and hard-codes `4px 4px`; the suite runs in Arabic
     and Hebrew, where a plate lit from the same side has to cast to the
     other one. Every offset in the scale is signed by this. */
  --shadow-x: 1;
}
[dir="rtl"] { --shadow-x: -1; }

/* ═══════════════════════════════════════════════════════════════
   Semantic layer — Frost (light)

   The default, and the sheet the whole system is measured against.
   Everything in it is astralaika.com's own paper and its own ink, by
   value: the page is the company's --paper settling into its --paper-2, a
   card is its --card, body ink is its --ink-soft, and full ink is its
   --ink. Put the two sites side by side and the stock is the same.

   What is *not* borrowed is the material. The company prints on that
   paper; the suite floats lenses over it.
   ═══════════════════════════════════════════════════════════════ */
:root,
[data-theme="light"] {
  color-scheme: light;

  /* ── The two tones every mirrored value is built from ──────────
     --pane-rgb is the tone on the *same* side of the ramp as the surface:
     what a piece of glass is tinted with. --edge-rgb is the tone on the
     *opposite* side: what a rule, a hover, a hairline or a grain is drawn
     in. The dark theme below is these two lines swapped, and because every
     border, every hover and every grain in the system is one of them at a
     fixed alpha, the two themes carry identical separation by construction
     rather than by two sets of hand-tuned numbers. */
  --pane-rgb: 251 247 236;   /* astralaika.com --card */
  --edge-rgb: 22 19 14;      /* astralaika.com --ink  */

  /* The sheet, and it is one flat colour: astralaika.com's --paper,
     #F3EDE0, the same value across the whole page the way the company
     prints it. It used to be a gradient descending into --paper-2, which
     sounded like depth and measured like drift — sampled at nine points
     the hub came out between #E9DBC5 and #EEDCCD while the company site
     came out #F3EDE0 at eight of the nine. A sheet is a sheet. The
     modulation belongs to the wash on top of it, to the banded section,
     and to the panes; not to the paper. */
  --field: var(--field-wash), #f3ede0;
  --field-grain: rgb(var(--edge-rgb) / 0.032);

  /* Halo alphas, and the strength each one buys against the bare sheet.
     Held at roughly what astralaika's own sunburst measures against its
     own paper — 1.09:1 — so the suite's field modulates the company's
     stock by about as much as the company does. */
  --halo-red:    0.042;  /* 1.06:1 */
  --halo-gold:   0.048;  /* 1.03:1 */
  --halo-ember:  0.036;  /* 1.04:1 */
  --halo-copper: 0.030;  /* 1.03:1 */

  --surface:        var(--ls-sand-0);    /* astralaika.com --card    */
  --surface-raised: var(--ls-sand-0);
  --surface-sunken: var(--ls-sand-50);   /* astralaika.com --paper   */
  --surface-inset:  var(--ls-sand-100);  /* astralaika.com --paper-2 */

  /* Glass tints — the card colour, held well below opaque so the warm
     field is visible *through* the pane rather than merely behind it. */
  --glass:        rgb(var(--pane-rgb) / 0.62);
  --glass-strong: rgb(var(--pane-rgb) / 0.82);
  --glass-subtle: rgb(var(--pane-rgb) / 0.40);
  --glass-lift:   1.06;

  /* The ink ramp. --text and --text-secondary are the company's own two
     inks, unmodified. --text-muted and --text-faint are solved rather than
     picked: muted is the point on the same warm line that measures 4.8:1
     on the light pane — the old --ls-sand-500 measured 4.39:1 and failed
     AA for the 11px technical labels that wear it — and faint is the point
     that measures 3:1. The dark theme's pair are the solutions to the same
     two equations on the dark pane, which is why they are not simply the
     ramp read backwards. */
  --text:          #16130e;   /* astralaika.com --ink      · 16.9:1 on glass */
  --text-secondary:#57503f;   /* astralaika.com --ink-soft ·  7.3:1          */
  --text-muted:    #69604f;   /*                              5.0:1          */
  --text-faint:    #9a8f78;   /*                              3.0:1          */
  --text-inverse:  var(--ls-sand-0);

  /* Rules. --border is astralaika's --line, value for value: the hairline
     it separates a menu row or edges a stamp with. --border-ink is its
     --ink used as a rule, undiluted, which is what its plates, its
     buttons, its controls and its section breaks are drawn in. There is
     nothing in between on that site and there is nothing in between here. */
  --line:          rgb(var(--edge-rgb) / 0.16);
  --ink-solid:     rgb(var(--edge-rgb));
  --border:        var(--line);
  --border-strong: rgb(var(--edge-rgb) / 0.55);
  --border-faint:  rgb(var(--edge-rgb) / 0.08);
  --border-ink:    var(--ink-solid);

  --hover:    rgb(var(--edge-rgb) / 0.055);
  --active:   rgb(var(--edge-rgb) / 0.095);
  --selected: color-mix(in srgb, var(--accent) 16%, transparent);

  /* The two company accents, as *ink on this sheet* rather than as fixed
     values. Cosmic red at #C8321E measures 4.89:1 on light glass and only
     3.35:1 on dark, so the pair is solved the way every other pair in this
     file is: one number, matched, at both ends of the ramp. A block that
     inverts its own sheet — the bulletin, the telegram plate, the footer —
     flips them back, in launcher.css, beside the block that does it. */
  --brand-red:  var(--ls-ember-600);   /* 4.89:1 */
  --brand-gold: var(--ls-gold-700);    /* 7.39:1 on paper */

  --scrim: rgb(var(--edge-rgb) / 0.45);

  /* Elevation — the company's offset square of ink, at its own five
     distances. A plate does not float above the page here; it sits on it
     and casts a hard shadow the way a printed card on a desk does. The
     alpha is astralaika's --line, 0.16, which is what its own menu and its
     own directive cards cast. */
  --shadow-xs: calc(2px * var(--shadow-x))  2px  0 var(--line);
  --shadow-sm: calc(4px * var(--shadow-x))  4px  0 var(--line);
  --shadow-md: calc(6px * var(--shadow-x))  6px  0 var(--line);
  --shadow-lg: calc(8px * var(--shadow-x))  8px  0 var(--line);
  --shadow-xl: calc(12px * var(--shadow-x)) 12px 0 var(--line);
  /* The solid version, for the hover state the company gives its buttons:
     four pixels of undiluted ink, and the plate steps up-left to meet it. */
  --shadow-hard: calc(4px * var(--shadow-x)) 4px 0 var(--ink-solid);
  --shadow-hard-accent: calc(4px * var(--shadow-x)) 4px 0 var(--accent);

  /* The lit edge. A pane catches light on its top bevel and drops it on
     the bottom one, and a specular highlight is white in *both* themes —
     so the two edges are not individually mirrored. What is mirrored is
     the tonal span across the pane, top edge to bottom edge: 1.33:1 in
     the light theme, 1.45:1 in the dark. It is the single detail that
     separates a lens from a rectangle of translucent paint. */
  --glass-edge:  rgb(255 255 255 / 0.85);
  --glass-under: rgb(var(--edge-rgb) / 0.10);
  --shadow-sheen: inset 0 1px 0 var(--glass-edge),
    inset 0 -1px 0 var(--glass-under);

  /* The ink on a filled plate, and it is the company's paper rather than
     pure white — this palette has no pure white in it and never has. */
  --accent-contrast: var(--ls-sand-0);
}

/* ═══════════════════════════════════════════════════════════════
   Semantic layer — Obsidian (dark)

   The light theme with the ramp read from the other end, and nothing
   else. Same composition, same wash, same halos at the same alpha, same
   glass weight, same lift off the field — the two lines below are the
   light theme's two lines swapped, and almost everything else follows
   from them without being restated.

   The company site already contains this theme: its bulletin strip, its
   footer and its telegram plate are `background: var(--ink); color:
   var(--paper)`. Obsidian is that band, enlarged to a whole interface and
   made of glass.

   Never pure black. astralaika's ink is #16130E and that is the floor —
   pure black kills the refraction the system is built on and makes the
   ember field look like a rendering bug.
   ═══════════════════════════════════════════════════════════════ */
[data-theme="dark"] {
  color-scheme: dark;

  --pane-rgb: 22 19 14;      /* astralaika.com --ink  */
  --edge-rgb: 251 247 236;   /* astralaika.com --card */

  /* One flat sheet, exactly as the light theme has one. It sits the same
     distance off --surface that the light sheet sits off its own card —
     1.11:1 against 1.09:1 — so a document, a dialog and a pane stand off
     the page by the same amount at both ends of the day. */
  --field: var(--field-wash), #221f19;
  --field-grain: rgb(var(--edge-rgb) / 0.032);

  /* The same four strengths, solved against ink instead of against paper.
     Read the second column, not the first: the alphas differ by a factor
     of two in both directions and the halos are identical. */
  --halo-red:    0.088;  /* 1.06:1 */
  --halo-gold:   0.023;  /* 1.03:1 */
  --halo-ember:  0.043;  /* 1.04:1 */
  --halo-copper: 0.029;  /* 1.03:1 */

  --surface:        var(--ls-sand-950);  /* astralaika.com --ink */
  --surface-raised: var(--ls-sand-950);
  --surface-sunken: var(--ls-sand-900);
  --surface-inset:  var(--ls-sand-800);

  --glass:        rgb(var(--pane-rgb) / 0.62);
  --glass-strong: rgb(var(--pane-rgb) / 0.82);
  --glass-subtle: rgb(var(--pane-rgb) / 0.40);
  /* Down where the light theme goes up, and by the amount that lifts the
     pane off the field by the same 1.13:1 the light one manages at 1.06. */
  --glass-lift:   0.86;

  /* The mirror of the light ramp, solved against the dark pane rather than
     assumed: --text is the company's --card, --text-secondary its --faded,
     and muted and faint are the points on the same warm line that measure
     5:1 and 3:1 — the two numbers the light theme's own muted and faint
     measure. Reading the ramp backwards by index would have put muted at
     5.6:1 and faint at 3.7:1, which is how a dark theme ends up feeling
     louder than its light twin without anybody being able to say why. */
  --text:          var(--ls-sand-0);   /* astralaika.com --card  · 16.7:1 */
  --text-secondary:var(--ls-sand-300); /* astralaika.com --faded ·  7.6:1 */
  --text-muted:    #9a9079;            /*                           5.0:1 */
  --text-faint:    #6b6352;            /*                           3.0:1 */
  --text-inverse:  var(--ls-sand-950);

  --line:          rgb(var(--edge-rgb) / 0.16);
  --ink-solid:     rgb(var(--edge-rgb));
  --border:        var(--line);
  --border-strong: rgb(var(--edge-rgb) / 0.55);
  --border-faint:  rgb(var(--edge-rgb) / 0.08);
  --border-ink:    var(--ink-solid);

  --hover:    rgb(var(--edge-rgb) / 0.055);
  --active:   rgb(var(--edge-rgb) / 0.095);
  --selected: color-mix(in srgb, var(--accent) 16%, transparent);

  --brand-red:  var(--ls-ember-500);   /* 4.59:1 */
  --brand-gold: var(--ls-gold-500);    /* 7.93:1 on ink */

  /* The one number that is deliberately not mirrored. A scrim's job is to
     put the page behind the dialog, and a dark page needs more of it to
     recede by the same amount. */
  --scrim: rgb(11 8 6 / 0.62);

  /* The same five offsets at the same five distances. The shadow is cast
     in the theme's own rule colour, so where the light theme drops ink on
     paper the dark one drops paper on ink. */
  --shadow-xs: calc(2px * var(--shadow-x))  2px  0 var(--line);
  --shadow-sm: calc(4px * var(--shadow-x))  4px  0 var(--line);
  --shadow-md: calc(6px * var(--shadow-x))  6px  0 var(--line);
  --shadow-lg: calc(8px * var(--shadow-x))  8px  0 var(--line);
  --shadow-xl: calc(12px * var(--shadow-x)) 12px 0 var(--line);
  --shadow-hard: calc(4px * var(--shadow-x)) 4px 0 var(--ink-solid);
  --shadow-hard-accent: calc(4px * var(--shadow-x)) 4px 0 var(--accent);

  /* Same specular, same span across the pane: 1.45:1 against the light
     theme's 1.33:1. A white highlight barely shows on near-white glass and
     shows plainly on near-black glass, which is what a highlight does — so
     the alphas differ and the measured result is what matches. */
  --glass-edge:  rgb(var(--edge-rgb) / 0.12);
  --glass-under: rgb(0 0 0 / 0.18);
  --shadow-sheen: inset 0 1px 0 var(--glass-edge),
    inset 0 -1px 0 var(--glass-under);

  --accent-contrast: var(--ls-sand-0);
}

/* ═══════════════════════════════════════════════════════════════
   Product layer — each application declares its hue once.
   Everything downstream (focus rings, selection, gradients, the
   installed-app theme colour) derives from --accent automatically.
   ═══════════════════════════════════════════════════════════════ */
:root {
  /* Suite default: the company's own cosmic red. Each application
     overrides it with its product hue below; only the hub keeps this, and
     the hub is astralaika.com's page — so its accent is astralaika.com's
     --red and its hover is astralaika.com's --red-dark, by value.

     It used to be --ls-ember-500 (#E05128), a step brighter, and the
     difference was not cosmetic: paper on #E05128 measures 3.91:1 and
     fails AA for the words on a button, while paper on the company's own
     #C8321E measures 4.99:1. The company had already solved it. */
  /* Slides — orangé. Assez loin du rouge company pour ne pas être
     confondu avec lui, assez loin de l'or de Vector pour tenir à 16px. */
  --ls-flame-400: #ff9b57;
  --ls-flame-500: #f2682b;
  --ls-flame-600: #c44d18;

  --ls-ember-400: #f2994a;
  --ls-ember-500: #e05128;
  --ls-ember-600: #c8321e;   /* astralaika.com --red      */
  --ls-ember-700: #9e2412;   /* astralaika.com --red-dark */

  --accent:       var(--ls-ember-600);
  --accent-hover: var(--ls-ember-700);
  --accent-soft:  var(--ls-ember-500);
  --accent-wash:  color-mix(in srgb, var(--accent) 10%, transparent);
  --accent-ring:  color-mix(in srgb, var(--accent) 42%, transparent);
  --accent-gradient: linear-gradient(135deg,
    var(--accent-soft) 0%, var(--accent) 46%, var(--accent-hover) 100%);
  /* The iridescent sweep used on the suite mark and hero surfaces. */
  /* The signature sweep: ember to gold, anchored by the company's own red.
     Deliberately not the cyan-blue-violet gradient the category defaults
     to — that is a competitor's signature, not ours. */
  --iridescence: linear-gradient(115deg,
    #c8321e 0%, #e85f2c 32%, #f2994a 64%, #f6c453 100%);
}

[data-app="write"] {
  --accent: var(--ls-azure-500);
  --accent-hover: var(--ls-azure-600);
  --accent-soft: var(--ls-azure-400);
}
[data-app="sheets"] {
  --accent: var(--ls-khaki-500);
  --accent-hover: var(--ls-khaki-600);
  --accent-soft: var(--ls-khaki-400);
}
[data-app="slides"] {
  /* Orangé, pas rouge : le rouge de la company est maintenant celui de
     LaikaSuite, et deux marques ne peuvent pas le porter. */
  --accent: var(--ls-flame-500);
  --accent-hover: var(--ls-flame-600);
  --accent-soft: var(--ls-flame-400);
}
[data-app="whiteboard"] {
  --accent: var(--ls-aqua-500);
  --accent-hover: var(--ls-aqua-600);
  --accent-soft: var(--ls-aqua-400);
}
[data-app="calendar"] {
  --accent: var(--ls-emerald-500);
  --accent-hover: var(--ls-emerald-600);
  --accent-soft: var(--ls-emerald-400);
}
[data-app="suite"] {
  --accent: #c8321e;
  --accent-hover: #9e2412;
  --accent-soft: #e0452f;
}
[data-app="vault"] {
  --accent: var(--ls-coral-600);
  --accent-hover: var(--ls-coral-500);
  --accent-soft: var(--ls-coral-400);
}
[data-app="video"] {
  --accent: var(--ls-indigo-500);
  --accent-hover: var(--ls-indigo-600);
  --accent-soft: var(--ls-indigo-400);
}
[data-app="vector"] {
  --accent: var(--ls-gold-600);
  --accent-hover: var(--ls-gold-500);
  --accent-soft: var(--ls-gold-400);
}
[data-app="notes"] {
  --accent: var(--ls-violet-500);
  --accent-hover: var(--ls-violet-600);
  --accent-soft: var(--ls-violet-400);
}
[data-app="pdf"] {
  --accent: var(--ls-teal-500);
  --accent-hover: var(--ls-teal-600);
  --accent-soft: var(--ls-teal-400);
}
[data-app="dev"] {
  --accent: var(--ls-leaf-500);
  --accent-hover: var(--ls-leaf-600);
  --accent-soft: var(--ls-leaf-400);
}
[data-app="photo"] {
  --accent: var(--ls-fuchsia-500);
  --accent-hover: var(--ls-fuchsia-600);
  --accent-soft: var(--ls-fuchsia-400);
}
[data-app="packets"] {
  --accent: var(--ls-petrol-500);
  --accent-hover: var(--ls-petrol-600);
  --accent-soft: var(--ls-petrol-400);
}
[data-app="student"] {
  --accent: var(--ls-crimson-500);
  --accent-hover: var(--ls-crimson-600);
  --accent-soft: var(--ls-crimson-400);
}

/* Red at full saturation fails AA for small text on white; the on-light
   variant is darkened so Slides keeps the same contrast guarantee as its
   sibling applications. */
/* Petrol is the one product hue chosen for being dark, which is exactly
   wrong against a dark surface: the accent has to come up a step there or
   a primary button reads as a hole in the page. */
[data-app="packets"][data-theme="dark"] {
  --accent: var(--ls-petrol-400);
  --accent-hover: #7fadde;
  --accent-soft: #8fbbe6;
}

[data-app="slides"]:not([data-theme="dark"]) {
  /* Le ton clair de l'orangé ne passe pas AA sur la crème, donc le thème
     clair prend le cran foncé — même correctif que pour les autres hues,
     mais en orangé maintenant que le rouge appartient à LaikaSuite. */
  --accent: var(--ls-flame-600);
  --accent-hover: #a33f12;
}
[data-app="sheets"]:not([data-theme="dark"]) {
  --accent: var(--ls-khaki-600);
  --accent-hover: #586523;
}
[data-app="calendar"]:not([data-theme="dark"]) {
  --accent: var(--ls-emerald-600);
  --accent-hover: #096043;
}
[data-app="whiteboard"]:not([data-theme="dark"]) {
  --accent: var(--ls-aqua-600);
  --accent-hover: #007e97;
}
[data-app="dev"]:not([data-theme="dark"]) {
  --accent: var(--ls-leaf-600);
  --accent-hover: #205a17;
}

/* ═══════════════════════════════════════════════════════════════
   Display type for the scripts Anton does not cover.

   The bundled Anton is the Latin-1 subset — verified, not assumed: every
   glyph outside U+0000–00FF falls back. Left alone, a Polish or Czech
   headline would swap face mid-word (ČEŠTINA), and a Russian or Japanese
   one would render in a 400-weight system font and look thin beside its
   English sibling.

   So each uncovered script names its own heavy face and its own weight,
   exactly the way astralaika.com does it. The suite would rather show a
   different heavy face than a broken one, and no new font is downloaded
   to achieve it. Adding the latin-ext and Cyrillic Anton subsets to
   packages/design-system/fonts/ is what retires the first two rules.
   ═══════════════════════════════════════════════════════════════ */
:root:lang(pl), :root:lang(cs), :root:lang(hu), :root:lang(ro), :root:lang(tr) {
  --font-display: "Arial Black", "Helvetica Neue", "Noto Sans", "DejaVu Sans",
    system-ui, sans-serif;
  --font-display-weight: 900;
}
:root:lang(vi) {
  --font-display: "Arial Black", "Be Vietnam Pro", "Noto Sans", "DejaVu Sans",
    system-ui, sans-serif;
  --font-display-weight: 900;
}
:root:lang(ru), :root:lang(uk), :root:lang(el) {
  --font-display: "Arial Black", "Noto Sans", "DejaVu Sans", system-ui, sans-serif;
  --font-display-weight: 900;
}
:root:lang(zh) {
  --font-display: "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei",
    "Noto Sans CJK SC", "Source Han Sans SC", sans-serif;
  --font-display-weight: 800;
}
:root:lang(ja) {
  --font-display: "Hiragino Kaku Gothic ProN", "Yu Gothic", Meiryo,
    "Noto Sans CJK JP", "Source Han Sans JP", sans-serif;
  --font-display-weight: 800;
}
:root:lang(ko) {
  --font-display: "Apple SD Gothic Neo", "Malgun Gothic", "Noto Sans CJK KR",
    "Source Han Sans KR", sans-serif;
  --font-display-weight: 800;
}
:root:lang(ar) {
  --font-display: "Noto Naskh Arabic", "Geeza Pro", Tahoma, "Segoe UI", sans-serif;
  --font-display-weight: 700;
}
:root:lang(hi) {
  --font-display: "Noto Sans Devanagari", "Nirmala UI", "Kohinoor Devanagari",
    sans-serif;
  --font-display-weight: 700;
}
:root:lang(th) {
  --font-display: "Noto Sans Thai", "Leelawadee UI", Thonburi, sans-serif;
  --font-display-weight: 700;
}
/* Marks in these scripts stack above the caps or hang below the baseline,
   so display leading and uppercasing are relaxed for them — that rule
   lives with the display recipe in components.css, where the recipe is. */

/* ═══════════════════════════════════════════════════════════════
   Reduced motion — respected globally, declared once.
   Transitions collapse to near-zero rather than 0 so state changes
   still register as changes to assistive tech and to the eye.
   ═══════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  :root {
    --duration-instant: 1ms;
    --duration-fast:    1ms;
    --duration-normal:  1ms;
    --duration-slow:    1ms;
    --duration-slower:  1ms;
    --ease-spring: var(--ease-out);
  }
}

/* Forced-colours (Windows high contrast): drop decorative translucency
   and let the OS palette drive the interface. The lit edge and the grain
   go with it — a specular highlight the user cannot see the point of is
   just a stray line across the top of every pane. */
@media (forced-colors: active) {
  :root {
    --glass: Canvas;
    --glass-strong: Canvas;
    --glass-subtle: Canvas;
    --border: CanvasText;
    --border-strong: CanvasText;
    --border-ink: CanvasText;
    --field-grain: transparent;
    --field-rays: none;
    --shadow-sheen: none;
    --glass-filter: none;
    --glass-filter-heavy: none;
  }
}
