/* =========================================================================
   base.css — Reset, document defaults, layout primitives, utilities
   iDev Digital Library Portal — Wireframe Pack v1

   Depends on tokens.css. Contains no colour/size literals of its own
   other than structural 1px/percentage values.

   RTL policy (contract §5): logical properties ONLY. There is no
   physical left/right anywhere in this file; the whole pack mirrors
   from <html dir="rtl"> alone.

   Arabic script policy: letter-spacing is NEVER written as a literal
   here. Arabic is cursive — positive tracking splits the connected
   baseline and negative tracking overlaps glyphs. All tracking comes
   from the --tracking-* tokens, which tokens.css §1 resets to `normal`
   for :root[lang="ar"], :root[dir="rtl"].

   Breakpoints (used consistently across the pack — media queries cannot
   read custom properties, so these are fixed by convention):
     1200px — wide desktop  → 4-col grids drop to 3
      960px — small desktop → 3/4-col grids drop to 2, sidebar stacks
      720px — tablet        → 2-col grids stack, .hide-sm disappears
      480px — phone         → everything single-column

   Structure:
     1. Reset & document defaults
     2. Typography rhythm
     3. Focus & selection
     4. Layout primitives (.stack .row .cluster .spread .grid …)
     5. Utilities
     6. Placeholder blocks (.ph)
     7. Responsive behaviour
     8. Reduced motion
   ========================================================================= */

/* ------------------------------------------------------------------ */
/* 1. Reset & document defaults                                       */
/* ------------------------------------------------------------------ */

*,
*::before,
*::after {
  box-sizing: border-box;
}

/* Vertical rhythm in this pack is gap-driven (.stack/.grid), so default
   margins are removed everywhere and never re-introduced piecemeal. */
* {
  margin: 0;
}

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  font-family: var(--font-ui);
  font-size: var(--fs-md);
  line-height: var(--lh-body);
  color: var(--text);
  background: var(--bg);
  min-block-size: 100vh;
  -webkit-font-smoothing: antialiased;
}

/* Media never overflows its container. */
img,
video,
canvas {
  display: block;
  max-inline-size: 100%;
}

/* Form elements inherit typography; nothing renders in browser default
   fonts. Their visual styling lives in components.css. */
input,
button,
select,
textarea {
  font: inherit;
  color: inherit;
}

/* Semantic lists keep their indent (CMS/content pages); component
   lists (.list, .tree, .facet-list …) reset it themselves. */
ul,
ol {
  padding-inline-start: 1.4em;
}

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

/* Links take --primary-ink, the ON-SURFACE TEXT role (tokens.css §3).
   --primary is the FILL role and is not required to clear AA against
   --surface / --surface-2, so it must never be used as a foreground.
   In light themes --primary-ink is a plain alias of --primary, so this
   is a no-op there; dark themes re-derive it per tenant to clear 4.5:1. */
a {
  color: var(--primary-ink);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

table {
  border-collapse: collapse;
}

/* ------------------------------------------------------------------ */
/* 2. Typography rhythm                                               */
/* ------------------------------------------------------------------ */

h1, h2, h3, h4, h5, h6 {
  line-height: var(--lh-heading);
  font-weight: 700;
  color: var(--text);
  overflow-wrap: break-word;
}

/* Tracking is tokenised (tokens.css §1) because Arabic is cursive: any
   non-zero letter-spacing breaks the joined baseline. The Arabic block in
   tokens.css resets every --tracking-* to `normal`, so these declarations
   are self-neutralising and must never be given literal values here. */
h1 { font-size: var(--fs-2xl); letter-spacing: var(--tracking-tight); }
h2 { font-size: var(--fs-xl);  letter-spacing: var(--tracking-tight-2); }
h3 { font-size: var(--fs-lg); }
h4 { font-size: var(--fs-md); }
h5 { font-size: var(--fs-sm); }

/* h6 doubles as an "eyebrow"/overline label in dense admin layouts. */
h6 {
  font-size: var(--fs-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--text-muted);
}

p {
  overflow-wrap: break-word;
}

/* Keep embedded Latin runs legible inside Arabic text (contract §5.5:
   authors wrap them in <span dir="ltr" class="mono">). */
code,
kbd,
.mono {
  font-family: var(--font-mono);
  font-size: 0.92em;
}

/* ------------------------------------------------------------------ */
/* 3. Focus & selection                                               */
/* ------------------------------------------------------------------ */

/* One visible, consistent focus ring for every interactive element.
   Components may restyle it but never remove it without replacement.
   The colour is --focus-ring, which tokens.css re-derives to the pale
   --primary-ink in dark so the indicator clears the 3:1 floor of WCAG
   1.4.11; --primary alone does not. This is the fallthrough ring for
   anything without a component rule (bare <a>, [tabindex], summary),
   and matches the var(--focus-ring, var(--text)) form used by every
   other outline declaration in the pack. */
:focus-visible {
  outline: 2px solid var(--focus-ring, var(--text));
  outline-offset: 2px;
}

::selection {
  background: var(--primary);
  color: var(--primary-fg);
}

::placeholder {
  color: var(--text-faint);
  opacity: 1;
}

/* ------------------------------------------------------------------ */
/* 4. Layout primitives                                               */
/*    All gaps are driven by the --gap custom property, e.g.          */
/*    <div class="stack" style="--gap:24px">. Allowed values:         */
/*    4 8 12 16 24 32 48 (px). Default: 16px.                         */
/* ------------------------------------------------------------------ */

/* Vertical flow. */
.stack {
  display: flex;
  flex-direction: column;
  gap: var(--gap, var(--sp-4));
}

/* Horizontal flex line, vertically centred, no wrapping. */
.row {
  display: flex;
  align-items: center;
  gap: var(--gap, var(--sp-4));
}

/* Wrapping row — chips, tag lists, button clusters. */
.cluster {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--gap, var(--sp-2));
}

/* Space-between line — headers, list rows, footers. */
.spread {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap, var(--sp-4));
}

/* Grid family. minmax(0, 1fr) so long content can shrink and
   .truncate works inside cells. */
.grid {
  display: grid;
  gap: var(--gap, var(--sp-4));
}

.grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

/* Content + rail. Rail width is overridable per use:
   <div class="grid grid--sidebar" style="--rail:280px">.
   Collapses to a single column under 960px (see §7). */
.grid--sidebar {
  grid-template-columns: minmax(0, 1fr) var(--rail, 320px);
}

/* Centring wrapper — sign-in cards, empty pages, hero content. */
.center {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--gap, var(--sp-4));
  text-align: center;
}

/* Flexible spacer / greedy child inside .row and .spread. */
.fill {
  flex: 1 1 auto;
  min-inline-size: 0;
}

.sticky-top {
  position: sticky;
  inset-block-start: 0;
  z-index: 5;
}

/* ------------------------------------------------------------------ */
/* 5. Utilities                                                       */
/* ------------------------------------------------------------------ */

.muted  { color: var(--text-muted); }
.small  { font-size: var(--fs-xs); }
/* Inline emphasis. Tokenised, and deliberately not heavier than the
   --fw-semibold used by .card-title / .section-title / .stat-value:
   an emphasised word inside body copy must not outweigh the heading
   above it. 650 is not a shipped face (tokens.css §2 names it as the
   counter-example) and rounded up to 700. */
.strong { font-weight: var(--fw-semibold); }

.truncate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-inline-size: 100%;
}

.nowrap { white-space: nowrap; }

/* Visually hidden, still exposed to assistive technology. */
.sr-only {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* .hide-sm — hidden under 720px; defined with the breakpoints in §7. */

/* ------------------------------------------------------------------ */
/* 6. Placeholder blocks                                              */
/*    CSS-drawn stand-ins for imagery (contract forbids image files). */
/*    Tonal fill + fine diagonal hatch + a small "frame" glyph so     */
/*    they read as deliberate placeholders, never as broken assets.   */
/* ------------------------------------------------------------------ */

/* v3. The v2 placeholder was a grey box under a 45-degree hatch with a
   small empty-frame glyph in the middle. The intent was "obviously a
   deliberate placeholder"; the result, at real size on a real page, was
   a broken image — and on w-home three of them sat side by side in the
   most prominent block of the homepage.

   The fix is not more hatching, it is to draw the OBJECT. A cover is a
   tinted jacket with a spine and a band where the title sits; a wide
   placeholder is a plate. Both are unmistakably drawn rather than
   photographed, so they still read as stand-ins — but as stand-ins for
   something, which is the difference between "not built yet" and
   "broken". Tint comes from --primary, so every placeholder in the pack
   re-skins with the tenant. */
.ph {
  position: relative;
  display: grid;
  place-items: center;
  min-block-size: 64px;
  border-radius: var(--r-md);
  background-image:
    linear-gradient(150deg,
      color-mix(in srgb, var(--primary) 13%, var(--surface)) 0%,
      color-mix(in srgb, var(--primary) 5%, var(--surface)) 55%,
      color-mix(in srgb, var(--primary) 9%, var(--surface)) 100%);
  background-color: var(--surface-2);
  box-shadow: inset 0 0 0 var(--border-w)
              color-mix(in srgb, var(--primary) 16%, transparent);
  color: var(--text-faint);
  overflow: hidden;
  flex: none;
}

/* The title band a jacket carries — two ruled lines, not a glyph. */
.ph::after {
  content: "";
  inline-size: 56%;
  block-size: 2px;
  border-radius: var(--r-full);
  background: color-mix(in srgb, var(--primary) 26%, transparent);
  box-shadow: 0 6px 0 color-mix(in srgb, var(--primary) 16%, transparent);
}

/* Book / journal cover: portrait, with a darker spine on the inline-start
   edge and the jacket tint running across it. Mirrors in RTL for free. */
.ph--cover {
  aspect-ratio: 2 / 3;
  inline-size: 100%;
  min-block-size: 0;
  border-radius: var(--r-sm);
  align-content: start;
  padding-block-start: 26%;
  background-image:
    /* spine */
    linear-gradient(to var(--ph-spine-dir, right),
      color-mix(in srgb, var(--primary) 34%, var(--surface)) 0 5px,
      color-mix(in srgb, var(--primary) 20%, var(--surface)) 5px 7px,
      transparent 7px),
    linear-gradient(155deg,
      color-mix(in srgb, var(--primary) 17%, var(--surface)) 0%,
      color-mix(in srgb, var(--primary) 7%, var(--surface)) 60%,
      color-mix(in srgb, var(--primary) 12%, var(--surface)) 100%);
}

/* The spine sits on the reading-start edge, so its gradient direction
   flips with the document. This is the one place a physical direction is
   unavoidable — a gradient has no logical axis — so it is driven by a
   custom property set once, here, rather than by a rule per component. */
[dir="rtl"] .ph--cover { --ph-spine-dir: left; }

/* A cover has two jobs depending on where it sits. As a block child of a card
   it is a banner and should fill the width. As a flex child of a row it is a
   THUMBNAIL beside text — and there `inline-size: 100%` makes it swallow the
   whole row and tower over the copy at a 2:3 ratio. Size it explicitly in
   that context instead, and stop it flexing. */
.row > .ph--cover,
.list-item > .ph--cover,
.app-list-media > .ph--cover,
.app-list-item > .ph--cover {
  flex: 0 0 auto;
  inline-size: var(--cover-w, 52px);
}

/* Round avatar placeholder; size via --size. */
.ph--avatar {
  inline-size: var(--size, 40px);
  block-size: var(--size, 40px);
  min-block-size: 0;
  border-radius: 50%;
}

.ph--avatar::after {
  inline-size: 10px;
  block-size: 10px;
  border-radius: 50%;
}

/* Banner / hero media strip. */
.ph--wide {
  aspect-ratio: 16 / 6;
  inline-size: 100%;
  min-block-size: 0;
}

/* ------------------------------------------------------------------ */
/* 7. Responsive behaviour — 1200 / 960 / 720 / 480                   */
/* ------------------------------------------------------------------ */

@media (max-width: 1200px) {
  .grid--4 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (max-width: 960px) {
  .grid--3,
  .grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  .grid--sidebar { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 720px) {
  .grid--2 { grid-template-columns: minmax(0, 1fr); }

  .hide-sm { display: none !important; }
}

@media (max-width: 480px) {
  .grid--3,
  .grid--4 { grid-template-columns: minmax(0, 1fr); }
}

/* ------------------------------------------------------------------ */
/* 8. Reduced motion                                                  */
/* ------------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ------------------------------------------------------------------ */
/* Numerals — decided once, everywhere                                */
/* ------------------------------------------------------------------ */
/* Western digits in both locales (contract §2.3) and proportional
   figures wherever numbers are COMPARED rather than read in prose. A
   column of counts set in proportional figures does not align on the
   decimal and reads as sloppy at a glance; tabular fixes it for free.
   Prose keeps proportional figures, which is why this is scoped rather
   than global. */
td, th,
.stat-value, .stat-delta,
.kv-val, .badge,
.table, .pagination,
.mono, code, kbd {
  font-variant-numeric: tabular-nums;
}
