/* ============================================================================
   KSRTC DESIGN SYSTEM — BASE LAYER
   ----------------------------------------------------------------------------
   Reset · document · type scale · Malayalam guards · accessibility primitives.
   NO page components live here: every `c-` class belongs to components.css.

   Load order is fixed and must not change:
       tokens.css  →  base.css  →  components.css

   THE ONE RULE: this file names no literal colour anywhere — no hex triplet, no
   functional colour notation, nothing but semantic tokens from tokens.css. That
   is what lets all four themes (livery · thaneer · yathra · ticket) and the
   high-contrast layer keep working without this file ever being edited again.

   ── CONTEXT HOOKS for components.css ──────────────────────────────────────
   base.css cannot know which surface a component paints, so nothing here names
   a colour that depends on one. Links and the parallel-language line are both
   derived from `currentColor` — the ink the component has already certified
   against its own background — which makes them impossible to invert. That was
   not a stylistic choice: an earlier revision defaulted links to --accent-text
   and painted light-on-light inside a recessed .c-staff panel nested in the
   dark footer, measured at 1.09:1.

   Two optional hooks let a component that HAS certified its surface do better:

       --link-ink         link colour              (default: currentColor)
       --link-ink-hover   link hover colour        (default: currentColor)
       --ink-parallel     the parallel-language line in .u-bi / .u-bii
                          (default: currentColor — dimming is opt-in)

   Three ready-made bundles are below:
       .u-on-surface   light ground: brand-red links, --ink-2 parallel line
       .u-on-invert    dark band  : reset the hooks to the inherit-safe default
       .u-on-accent    red band   : reset the hooks to the inherit-safe default
   Put a reset bundle on any region nested inside a band that set the hooks.
   ========================================================================== */

/* ══════════════════════════════════════════════════════════════════════════
   1 · RESET
   ══════════════════════════════════════════════════════════════════════════ */
*,
*::before,
*::after { box-sizing: border-box; }

html {
  /* iOS inflates font sizes in landscape unless this is pinned; the fluid
     clamp() scale already handles size, so the UA must not double-adjust. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  /* All four themes are light. Declaring it stops Android Chrome / Samsung
     Internet from force-inverting form controls under an OS dark theme, which
     would put un-tokenised colours on the page. */
  color-scheme: light;
}

body, h1, h2, h3, h4, h5, h6, p, figure, blockquote,
dl, dd, ul, ol, fieldset, legend, table, pre { margin: 0; }

ul, ol { padding-inline-start: 1.5em; }
/* Lists used as layout (nav, card grids, footer columns) are always classed. */
ul[class], ol[class] { list-style: none; padding: 0; }

img, svg, picture, video, canvas, audio, iframe, embed, object {
  display: block;
  max-inline-size: 100%;
}
img, video { block-size: auto; }         /* honours the mandatory width/height */
svg { fill: currentColor; }              /* inline SVG icons only — no icon fonts */

/* Form controls do not inherit type by default; on a Malayalam page that
   silently drops labels and inputs back to a Latin UA font. */
input, button, select, textarea, optgroup {
  font: inherit;
  letter-spacing: inherit;
  color: inherit;
}
textarea { resize: vertical; }
button, [type="button"], [type="submit"], [type="reset"], summary { cursor: pointer; }
button {
  background: none;                      /* no colour named: resets to initial */
  border: 0;
  padding: 0;
  text-align: inherit;
}

/* The UA default is `min-inline-size: min-content`, which stops a fieldset from
   ever shrinking below its widest child and produces horizontal overflow at
   390px in any wrapping form. */
fieldset { min-inline-size: 0; border: 0; padding: 0; }
legend { padding: 0; }

table { border-collapse: collapse; border-spacing: 0; }

hr {
  border: 0;
  border-block-start: var(--rule-w) solid var(--rule);
}

/* An in-page anchor must not land under a sticky masthead. :target is the
   supported selector today; :target-within is kept as a separate rule so that
   browsers which do not know it simply drop that rule and not this one. */
:target { scroll-margin-block-start: var(--sp-6); }
:target-within { scroll-margin-block-start: var(--sp-6); }

/* ── RING COLOURS, CAPTURED AT :root ON PURPOSE ───────────────────────────
   A custom property is substituted where it is DECLARED, so these three resolve
   against the theme at the root element and are then inherited as fixed values.
   That immunises the focus ring from the standard component trick of re-pointing
   a token inside a band (`.c-band--invert { --ink: var(--ink-invert) }`), which
   would otherwise turn the ring's dark band light and collapse its two tones.
   Measured on the real homepage during integration: band-vs-band contrast fell
   to 1.24:1 inside the ticket theme's inverted bands before this existed. */
:root {
  --ring-dark: var(--ink);
  --ring-light: var(--surface-3);
  --ring-halo: var(--highlight);
}

/* ══════════════════════════════════════════════════════════════════════════
   2 · DOCUMENT
   The page is <html lang="ml">, so the Malayalam family and the Malayalam
   line-height are the DEFAULTS and Latin is the exception — not the reverse.
   ══════════════════════════════════════════════════════════════════════════ */
html { background-color: var(--surface); }

body {
  background-color: var(--surface);
  color: var(--ink);
  font-family: var(--font-ml);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  /* Long Malayalam compounds cannot be hyphenated; without this a single word
     can push the viewport sideways on a 390px phone. */
  overflow-wrap: break-word;
  hyphens: none;
}

[lang="en"] {
  font-family: var(--font-en);
  line-height: var(--lh-en-body);
}

/* A link inherits its container's ink, so it is legible on cream, on the dark
   footer and on a red band without base.css knowing which it is. That makes the
   UNDERLINE, not the colour, the thing that marks a link — which WCAG 1.4.1
   wants anyway. A component that strips the underline must therefore give its
   links another non-colour affordance (a card, a nav row, a button). */
a {
  color: var(--link-ink, currentColor);
  text-decoration-line: underline;
  text-decoration-thickness: from-font;
  text-underline-offset: 0.18em;
}
a:hover {
  color: var(--link-ink-hover, var(--link-ink, currentColor));
  /* thickens on any ground, so hover feedback survives a colour-blind reading
     and an inherited-ink context alike */
  text-decoration-thickness: max(2px, from-font);
}

::selection {
  background-color: var(--highlight);
  color: var(--ink);
}

/* Surface bundles — see the hook note in the file header. */
.u-on-surface {                          /* light ground: opt into brand red */
  --link-ink: var(--accent-text);
  --link-ink-hover: var(--accent-strong);
  --ink-parallel: var(--ink-2);
}
/* Reset to the inherit-safe defaults. `initial` on a custom property is the
   guaranteed-invalid value, so var(--ink-parallel, …) falls back to currentColor. */
.u-on-invert,
.u-on-accent {
  --link-ink: currentColor;
  --link-ink-hover: currentColor;
  --ink-parallel: initial;
}

/* ══════════════════════════════════════════════════════════════════════════
   3 · TYPE SCALE — bound to the tokens, never to px
   ══════════════════════════════════════════════════════════════════════════ */
h1, h2, h3, h4, h5, h6 {
  line-height: var(--lh-display);
  font-weight: 700;
  text-wrap: balance;
}
h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); }
h4, h5, h6 { font-size: var(--fs-body); }

p, li, td, dd, dt, figcaption, blockquote { line-height: var(--lh-body); }

/* Running prose only. Never on a grid, a nav or a table. */
.u-measure { max-inline-size: var(--measure); }

/* ══════════════════════════════════════════════════════════════════════════
   4 · MALAYALAM GUARDS — declared here so no component can regress them
   ----------------------------------------------------------------------------
   WHY each of these exists, because they are the rules most often broken:

   a) 15px FLOOR. Malayalam is a conjunct script: ക + ് + ത renders as a single
      stacked glyph (ക്ത). Below roughly 15px the ligature's joins close up and
      the vowel signs and chandrakkala clip against the line above, so the word
      stops being readable — not merely small. --fs-small IS that floor.
      --fs-micro (13px) is LATIN ONLY and must never reach Malayalam.
      max(--fs-small, 1em) is a floor, not a resize: an element already larger
      than 15px keeps its own size, so the type scale is untouched.

   b) BODY LINE-HEIGHT >= 1.7. Those stacked conjuncts occupy space above and
      below the x-height that Latin never uses. A Latin-tuned 1.4–1.5 makes
      consecutive Malayalam lines collide. --lh-body is 1.75.

   c) NEVER LETTER-SPACED. Tracking is inserted between the glyph clusters that
      a conjunct is made of, which breaks the join and renders a different,
      wrong sequence of letters. All tracking on this site is therefore scoped
      to [lang="en"]; the guard below re-normalises any that leaks in by
      inheritance. Uppercasing is likewise meaningless in Malayalam.
   ══════════════════════════════════════════════════════════════════════════ */

/* (a) the floor */
[lang="ml"] { font-size: max(var(--fs-small), 1em); }

/* Headings carry display sizes of their own; the floor must never flatten a
   <h2 lang="ml"> down to the inherited body size. */
h1[lang="ml"] { font-size: max(var(--fs-small), var(--fs-h1)); }
h2[lang="ml"] { font-size: max(var(--fs-small), var(--fs-h2)); }
h3[lang="ml"] { font-size: max(var(--fs-small), var(--fs-h3)); }
h4[lang="ml"], h5[lang="ml"], h6[lang="ml"] { font-size: max(var(--fs-small), var(--fs-body)); }

/* (b) body-context leading, whether the Malayalam is the host element itself
   or a <span lang="ml"> inside it. max() means a component cannot lower
   --lh-body underneath the guard either. */
p[lang="ml"], li[lang="ml"], td[lang="ml"], dd[lang="ml"], dt[lang="ml"],
figcaption[lang="ml"], blockquote[lang="ml"],
p [lang="ml"], li [lang="ml"], td [lang="ml"], dd [lang="ml"], dt [lang="ml"],
figcaption [lang="ml"], blockquote [lang="ml"] {
  line-height: max(1.7, var(--lh-body));
}

/* (c) tracking guard. A direct declaration always beats an inherited value, so
   this neutralises tracking set on any ancestor. */
[lang="ml"] {
  letter-spacing: normal;
  text-transform: none;
  font-variant-caps: normal;
}

/* ══════════════════════════════════════════════════════════════════════════
   5 · ACCESSIBILITY PRIMITIVES
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Skip link — must be the FIRST focusable element in the document ──────
   Off-screen by transform (not display:none, which would remove it from the
   tab order), fixed so no ancestor's overflow can clip it. */
.u-skip {
  position: fixed;
  z-index: 999;
  inset-block-start: 0;
  inset-inline-start: 0;
  display: inline-flex;
  align-items: center;
  min-block-size: var(--tap);
  padding: var(--sp-2) var(--sp-4);
  background-color: var(--ink);
  color: var(--surface-3);          /* near-black on near-white: >= 14:1 in every theme */
  font-weight: 700;
  text-decoration: none;
  border-block-end: var(--rule-w-strong, 3px) solid var(--highlight);
  transform: translateY(-200%);
  transition: transform var(--dur) var(--ease);
}
.u-skip:focus { transform: translateY(0); }
.u-skip:hover { color: var(--surface-3); }

/* ── THE FOCUS RING: THREE BANDS, BACKDROP-AGNOSTIC ───────────────────────
   A ring drawn with outline-offset sits OUTSIDE the element, on whatever is
   BEHIND it — not on the element's own background. A single-colour ring
   therefore cannot be certified: gold "verified against a red button" was a
   real defect in this project, because the offset ring never touched the red.

   The ring is built so that its contrast partners are bands WE control, not an
   unknown backdrop. From the element outwards:

       0–2px  --ring-light  (= root --surface-3) near-white; the only band
                            that touches the element
       2–5px  --ring-dark   (= root --ink)        near-black
       5–8px  --ring-halo   (= root --highlight)  gold (orange in yathra)

   box-shadow paints first-listed on top, so the wide halo layer shows only where
   the narrow light layer does not cover it, and the outline — which paints above
   both — occupies the 2–5px offset ring.

   Measured worst case across the four themes (WCAG 1.4.11 floor 3:1):
     against the ELEMENT's own fill (the band at 0–2px):
       --surface-3 vs --surface-accent   livery red   5.0:1   yathra red  6.6:1
       --surface-3 vs --surface-invert   footer      13.6:1
       --surface-3 vs --surface          cream        1.1:1 -> the --ink band
                                          at 2–5px carries it at 14.8:1
     against the BACKDROP (whatever the ring is painted over):
       --ink vs cream 14.8:1 · --surface-3 vs dark 13.6:1 · --ink vs red 3.5:1
     band against band:
       --surface-3 vs --ink 17:1 · --ink vs --highlight 5.5:1 (yathra, tightest)
   So on light, dark and red grounds alike the ring clears 3:1 against the
   element, against the backdrop, and between its own bands.
   Focus is never suppressed anywhere in this design system.

   The !important below is deliberate. `box-shadow` on a component has the same
   specificity as `:focus-visible` and components.css loads later, so a card's
   elevation shadow silently deletes two of the three bands. A focus indicator is
   a gated accessibility floor, not a decoration, so it wins. A component that
   genuinely needs a different ring must say so with its own !important — an
   explicit act, not an accident. */
:focus-visible {
  outline: 3px solid var(--ring-dark) !important;
  outline-offset: 2px !important;
  box-shadow:
    0 0 0 2px var(--ring-light),
    0 0 0 8px var(--ring-halo) !important;
}
/* Same ring for engines without :focus-visible (kept in @supports so that
   modern browsers do not show a ring on mouse-down). */
@supports not selector(:focus-visible) {
  :focus {
    outline: 3px solid var(--ring-dark) !important;
    outline-offset: 2px !important;
    box-shadow:
      0 0 0 2px var(--ring-light),
      0 0 0 8px var(--ring-halo) !important;
  }
}

/* ── Visually hidden, still announced. clip-path:inset(50%) is the form the
   contract gate recognises as legitimately hidden. ─────────────────────── */
.u-visually-hidden {
  position: absolute !important;
  inline-size: 1px;
  block-size: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── Touch targets: WCAG 2.5.5, >= 44x44 (--tap) ──────────────────────────
   min-block-size is inert on a genuinely inline link, so prose links keep
   their natural flow (WCAG 2.5.8 excepts them) while every block, flex or
   grid target grows. Checkbox and radio are excluded on purpose: stretching
   the box distorts it — enlarge their <label> with .u-tap instead. */
a[href], button, select, textarea, summary, [role="button"], [role="tab"],
input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]) {
  min-block-size: var(--tap);
}
button, select, summary, [role="button"], [role="tab"] {
  min-inline-size: var(--tap);
}
.u-tap {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-block-size: var(--tap);
  min-inline-size: var(--tap);
}

/* ══════════════════════════════════════════════════════════════════════════
   6 · BILINGUAL PRIMITIVES — Malayalam primary, English parallel (Clause 2.2)
   ----------------------------------------------------------------------------
   Markup, always Malayalam FIRST in the DOM:

       <span class="u-bi"><span lang="ml">…</span><span lang="en">…</span></span>

   [data-locale="en"] flips which line is dominant using `order` and type only.
   The DOM never changes, so the document's reading order stays Malayalam-first
   for a screen reader in both locales, and a component cannot desynchronise
   the two languages by re-ordering nodes.
   ══════════════════════════════════════════════════════════════════════════ */
.u-bi {
  display: flex;
  flex-direction: column;
  gap: 0.1em;
}
.u-bi > [lang="ml"] { order: 1; }
.u-bi > [lang="en"] {
  order: 2;
  /* --fs-micro is Latin-only, and .72em keeps the pair proportional inside a
     large heading. */
  font-size: max(var(--fs-micro), 0.72em);
  line-height: var(--lh-en-body);
  font-weight: 600;
  letter-spacing: 0.08em;                 /* tracking: Latin ONLY */
  text-transform: uppercase;
  /* COLOUR IS INHERITED, NOT DIMMED. The hierarchy here is carried by size,
     weight, caps and tracking — all of which are contrast-neutral — so this
     file can never turn a colour the component certified into one that fails.
     Dimming was tried and measured: muting toward --surface reads beautifully
     on primary ink (7.8:1) but on secondary ink it landed at 4.25:1 on the real
     homepage, i.e. base.css breaking a component's AA. Opt into a dimmed
     parallel line where the surface is known, with .u-on-surface or
     --ink-parallel: var(--ink-2). */
  color: var(--ink-parallel, currentColor);
}
[data-locale="en"] .u-bi > [lang="en"] {
  order: 1;
  font-size: 1em;
  font-weight: inherit;
  letter-spacing: normal;
  text-transform: none;
  color: inherit;
}
/* Demoted Malayalam is still Malayalam: the 15px floor and full leading hold. */
[data-locale="en"] .u-bi > [lang="ml"] {
  order: 2;
  font-size: max(var(--fs-small), 0.72em);
  line-height: max(1.7, var(--lh-body));
  font-weight: 600;
  color: var(--ink-parallel, currentColor);
}

/* TABLE INTEGRITY. `display: flex` on a <caption> destroys its table role: the
   caption stops being a caption and Chromium reflows it INSIDE the table, below
   the header row (seen and fixed during verification). Same for a <th>/<td>.
   Prefer wrapping the pair in a <span> inside the cell; this guard means that
   forgetting to costs only the locale flip — `order` needs a flex parent, so
   inside a table element the pair stays in DOM order, Malayalam first — and
   never the table's layout. */
caption.u-bi, caption.u-bii { display: table-caption; }
th.u-bi, th.u-bii { display: table-cell; }
td.u-bi, td.u-bii { display: table-cell; }
caption.u-bi > *, th.u-bi > *, td.u-bi > * { display: block; }

/* Inline pair, for one-line contexts (chips, meta rows, utility bar). */
.u-bii {
  display: inline-flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0 0.5ch;
  justify-content: flex-start;
}
.u-bii > [lang="ml"] { order: 1; }
.u-bii > [lang="en"] {
  order: 2;
  font-size: max(var(--fs-micro), 0.85em);
  color: var(--ink-parallel, currentColor);
}
.u-bii > [lang="en"]::before { content: "·"; padding-inline-end: 0.5ch; }
[data-locale="en"] .u-bii > [lang="en"] {
  order: 1;
  font-size: 1em;
  color: inherit;
}
[data-locale="en"] .u-bii > [lang="en"]::before { content: none; }
[data-locale="en"] .u-bii > [lang="ml"] {
  order: 2;
  font-size: max(var(--fs-small), 0.85em);
  color: var(--ink-parallel, currentColor);
}
[data-locale="en"] .u-bii > [lang="ml"]::before { content: "·"; padding-inline-end: 0.5ch; }

/* ══════════════════════════════════════════════════════════════════════════
   7 · PAGE CONTAINER — fluid, no breakpoint anywhere (Clause 2.3)
   ══════════════════════════════════════════════════════════════════════════ */
.u-wrap {
  inline-size: min(100%, var(--wrap));
  margin-inline: auto;
  padding-inline: clamp(var(--sp-4), 4vw, var(--sp-6));
}

/* ══════════════════════════════════════════════════════════════════════════
   8 · PRINT — a citizen printing a fare table or a depot contact
   ══════════════════════════════════════════════════════════════════════════ */
@media print {
  /* Chrome and controls carry no meaning on paper. */
  .u-skip,
  .c-utilitybar,
  .c-nav,
  [data-ks-theme], [data-ks-theme-select],
  [data-ks-textsize], [data-ks-textsize-step],
  [data-ks-contrast],
  [data-print="hide"] { display: none !important; }

  html, body {
    background: none;
    color: var(--ink);
  }
  * { box-shadow: none !important; text-shadow: none !important; }

  a { color: var(--ink); text-decoration: underline; }
  /* A printed page has no clickable links, so state the destination. */
  a[href^="http"]::after,
  a[href^="tel:"]::after,
  a[href^="mailto:"]::after {
    content: " (" attr(href) ")";
    font-family: var(--font-en);
    font-size: var(--fs-micro);          /* Latin only — a URL is never Malayalam */
    word-break: break-all;
  }
  h1, h2, h3, h4, caption { break-after: avoid; }
  table, figure, blockquote, tr, li { break-inside: avoid; }
  /* Reveal the whole fares table instead of a clipped scroller. */
  [role="region"][tabindex] { overflow: visible !important; }
  @page { margin: 14mm; }
}

/* ══════════════════════════════════════════════════════════════════════════
   9 · REDUCED MOTION — safety net
   tokens.css already sets --dur to 0ms, but that only disciplines transitions
   that actually read the token. This neutralises anything that does not,
   including a keyframe animation and smooth scrolling.
   ══════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto !important; }
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
    view-transition-name: none !important;
  }
}
