/* Lore — base: reset, tokens, typography, utilities. Mobile-first.
 *
 * ---- Breakpoint scale (R3) ----
 * CSS custom properties cannot be used inside @media queries, so this is a
 * documented literal scale instead — every file under assets/css/ should
 * reuse these exact numbers rather than inventing new ones:
 *
 *   360 / 390 / 414   phone   (QA test widths; no dedicated CSS breakpoint —
 *                              this is the unstyled/base mobile layout)
 *   768               tablet  (gutter step 24px -> wider; some components
 *                              start a column/grid change here)
 *   900               nav/chrome switch — the SITE-WIDE threshold where the
 *                      header's full nav + search appear and the mobile tab
 *                      bar disappears (layout.css, components.css, pages.css,
 *                      reader.css, awards.css all key off this one number —
 *                      keep new "desktop chrome" rules on 900, not 1024)
 *   1024              laptop  (QA test width; content grids may add a column)
 *   1280 / 1440       desktop (QA test widths)
 *   1920              wide    (QA test width)
 *
 * R3 fix: write.css previously switched at 640/780 while every other file
 * switched at 900 — writers on a 780-899px viewport got the *desktop*
 * write-editor layout underneath a header/tab-bar that still thought it was
 * *mobile*. write.css now uses 900 exclusively, matching the rest of the
 * site. A few components (`.lore-grid` column counts, the search sidebar,
 * the inbox split-view) key off content-fit breakpoints tuned to card/column
 * width (560/700/760/820/1100px) rather than device class — those are
 * intentionally left alone; they're not part of the seam bug and changing
 * them risks a real visual regression for no accessibility/responsive gain.
 *
 * ---- Z-index scale ----
 * Was 8 hardcoded literals with no shared meaning; now a small ordered set,
 * used everywhere in assets/css/ (2 unowned files — reader.css, coins.css —
 * still carry their own literals; see R3 hand-off notes).
 */
:root {
	/* Colour — R3 contrast pass. --ember/--teal are fill/graphic colours
	   (solid backgrounds, icon fills, borders) verified for white text at
	   >=4.5:1. --ember-text/--teal-text are darker variants for TEXT/links
	   set directly on a paper-family background, also verified >=4.5:1 on
	   both --paper and --paper-alt. --ember-dark now doubles as the
	   ember-light CHIP foreground (was 3.76:1, now >=4.5:1) as well as the
	   button hover fill. See R3_layout_spec hand-off notes for the full
	   before/after ratio table. */
	--ember: #C8431F;
	--ember-dark: #9c3a1c;
	--ember-light: #f8ddd2;
	--ember-text: #a6350f;
	--teal: #0f7a6e;
	--teal-dark: #157569;
	--teal-text: #157569;
	--ink: #1d1d1f;
	--ink-soft: #4a4a4d;
	--ink-faint: #5c5c60;
	--paper: #fbfaf7;
	--paper-alt: #f2efe9;
	--line: #e6e2da;
	--white: #ffffff;
	--danger: #c62828;
	--success: #1B998B;

	--radius-sm: 6px;
	--radius-md: 10px;
	--radius-lg: 16px;
	--radius-pill: 999px;

	--space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
	--space-5: 20px; --space-6: 24px; --space-8: 32px; --space-10: 40px; --space-12: 48px;

	--shadow-sm: 0 1px 2px rgba(29,29,31,.06), 0 1px 1px rgba(29,29,31,.04);
	--shadow-md: 0 4px 16px rgba(29,29,31,.10);
	--shadow-lg: 0 12px 32px rgba(29,29,31,.16);

	--header-h: 60px;
	--tabbar-h: 58px;
	/* R4: boxed-layout pass. Wattpad's real app surfaces (qa/r4/wattpad_layout_banners.md
	   §A) cap their content container at 1200px with a ~16px inner gutter, genuinely
	   full-width below that cap. Lore matches that exactly (was 1240px). Narrower
	   "panel" tiers nest inside/alongside it at their own fixed widths (settings
	   960px, reader prose 580px) — see .lore-container below for the shared rule. */
	--max-w: 1200px;
	/* R5: the boxed container's own side gutter, split out from --space-4
	   (which stays a general-purpose spacing token used all over the theme
	   for gaps/margins unrelated to this box) so it can step independently.
	   Wattpad measures 24px padding at ≥768px (1200px box -> 1152px visible
	   content) and 16px on phones (spec/R5_LAYOUT_TARGET.md §1). Every
	   selector in the shared allow-list below, plus the handful of files
	   that legitimately re-implement the same box (.lore-carousel,
	   .lore-cover-banner__gutter, .lore-reading-list-banner__gutter,
	   .lore-awards-hero), reads --gutter instead of --space-4 so this one
	   variable is the single source of truth for the box's inset. */
	--gutter: var(--space-4);

	/* Type scale (R3) — documents the sizes already in use across base.css/
	   components.css/pages.css so future headings pick from here instead of
	   a new one-off rem value. */
	--fs-h1: 1.75rem;   /* 28px */
	--fs-h2: 1.4rem;    /* 22.4px */
	--fs-h3: 1.15rem;   /* 18.4px */
	--fs-h4: 1rem;      /* 16px */
	--fs-body: 1rem;    /* 16px */
	--fs-sm: .875rem;   /* 14px */
	--fs-xs: .75rem;    /* 12px */

	/* Z-index scale (R3) — was hardcoded literals with no shared ordering. */
	--z-base: 1;
	--z-arrow: 2;
	--z-sticky: 5;
	--z-subheader: 20;
	--z-header: 40;
	--z-menu: 50;
	--z-popover: 60;
	--z-tabbar: 60;
	--z-hint: 150;
	--z-toast: 200;
	--z-overlay: 300;
	--z-sheet: 301;
	--z-skip-link: 1000;

	color-scheme: light;
}

/* R5: gutter step — matches the documented breakpoint scale's own note at
   the top of this file ("768 tablet (gutter step 24px -> wider...)").
   Below 768 every boxed surface keeps the phone gutter (16px, --space-4);
   at/above 768 it steps to the Wattpad-measured 24px (--space-6) and stays
   there through 1920 — the box itself stops growing at --max-w (1200px),
   so from ~1248px up the visible content is a flat 1152px everywhere. */
@media (min-width: 768px) {
	:root { --gutter: var(--space-6); }
}

@media (prefers-color-scheme: dark) {
	:root:not([data-app-theme="light"]) {
		--paper: #16161a;
		--paper-alt: #1e1e23;
		--ink: #f2f1ee;
		--ink-soft: #c9c7c2;
		--ink-faint: #9a9894;
		--line: #2c2c32;
		--white: #201f24;
		--ember-light: #3a251d;
		/* Dark mode flips --paper/--white to near-black, so the light-mode
		   "dark text on light paper" tokens above would be nearly invisible
		   here — give ember-dark/ember-text their own light-on-dark values,
		   verified >=4.5:1 against every dark-mode surface (incl. the
		   ember-light chip, which was still failing 4.33:1 with the naive
		   "just reuse light-mode ember-dark" approach). */
		--ember-dark: #e8815f;
		--ember-text: #e8815f;
		color-scheme: dark;
	}
}
:root[data-app-theme="dark"] {
	--paper: #16161a; --paper-alt: #1e1e23; --ink: #f2f1ee; --ink-soft: #c9c7c2;
	--ink-faint: #9a9894; --line: #2c2c32; --white: #201f24; --ember-light: #3a251d;
	--ember-dark: #e8815f; --ember-text: #e8815f;
	color-scheme: dark;
}
/* The `hidden` attribute must always win, even against a class that sets
   `display` (e.g. `.lore-btn[hidden]`) — same specificity as the UA
   stylesheet's own `[hidden]` rule means source order would otherwise let
   an author `.some-class { display: ... }` rule show it again. This bit
   several `[hidden]` toggles (publish/unpublish buttons, unread badge,
   install button) until caught in QA. */
[hidden] { display: none !important; }

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; scroll-padding-top: calc(var(--header-h) + 12px); }
@media (prefers-reduced-motion: reduce) {
	html { scroll-behavior: auto; }
	*, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; scroll-behavior: auto !important; }
}

body {
	margin: 0;
	background: var(--paper);
	color: var(--ink);
	font-family: var(--lore-font-ui);
	font-size: 16px;
	line-height: 1.5;
	-webkit-font-smoothing: antialiased;
	overflow-x: hidden;
}

img, svg { max-width: 100%; display: block; }
img { height: auto; }

h1, h2, h3, h4 { line-height: 1.25; margin: 0 0 .5em; font-weight: 700; }
h1 { font-size: var(--fs-h1); } h2 { font-size: var(--fs-h2); } h3 { font-size: var(--fs-h3); } h4 { font-size: var(--fs-h4); }
p { margin: 0 0 1em; }

a { color: inherit; text-decoration: none; }
a.lore-link { color: var(--ember-text); text-decoration: underline; text-underline-offset: 2px; }
/* R4 responsive audit (item 10): modifier for a .lore-link that stands
   alone as its own tap target (not embedded inline inside a sentence of
   surrounding prose, where enlarging just one run of words would break the
   paragraph's own layout — that inline case is a legitimate WCAG 2.5.8
   exception and is left alone). Reaches 44px with a real min-height rather
   than an invisible hit-area, since the link is already alone on its own
   line with room to grow. */
a.lore-link--cta { display: inline-flex; align-items: center; min-height: 44px; }

button { font: inherit; color: inherit; }

/* Focus visibility — never remove outlines, only restyle them. */
:focus-visible {
	outline: 2px solid var(--teal);
	outline-offset: 2px;
	border-radius: 4px;
}
a:focus-visible, button:focus-visible { outline-offset: 3px; }

.lore-skip-link {
	position: absolute; left: -999px; top: 0; z-index: var(--z-skip-link);
	background: var(--ember); color: #fff; padding: 10px 16px; border-radius: 0 0 8px 0;
}
.lore-skip-link:focus { left: 0; }

.screen-reader-text {
	position: absolute !important; width: 1px; height: 1px; overflow: hidden;
	clip: rect(1px,1px,1px,1px); white-space: nowrap; border: 0; padding: 0; margin: -1px;
}

/* Shared boxed-container rule (R4, gutter fixed R5) — the ONE place every
   top-level page surface gets its max-width/centering/gutter from, instead
   of repeating `max-width: var(--max-w); margin: 0 auto; padding: 0
   var(--gutter);` in a dozen+ separate component rules (that's what R3
   had, and it's how .lore-profile-head/.lore-settings/.lore-notif-list
   drifted off-system). Longhand margin-left/right + padding-left/right
   only (never the `margin`/`padding` shorthand) so this never clobbers a
   selector's own top/bottom spacing declared in its own file — add a class
   here, then in that selector's own rule keep only ITS vertical spacing /
   other properties (also longhand margin-top/margin-bottom only, for the
   same reason — see .lore-row/.lore-promo-strips in components.css/
   awards.css for the pattern). `.lore-container` itself is the plain
   utility for any one-off wrapper that doesn't already have a dedicated
   component class.

   R5 additions: `.lore-row` (was boxing only its `__head`, leaving the
   `__track` card rail to run full-bleed — qa/r5/LORE_LAYOUT_DEFECTS.md
   D1/D3/D4; boxing the row itself instead of just its head is what keeps
   the heading and the scrollable track sharing one edge, Wattpad's own
   technique) replaces `.lore-row__head` on this list — see components.css
   for why the head no longer needs its own box once its parent has one.
   `.lore-promo-strips` (D2), `.lore-notif-list-wrap` (D8) and
   `.lore-status-page` (D9) are newly boxed unwrapped/independently-narrow
   surfaces. `.lore-awards-category__title` (D6) is boxed too, but as its
   own two-layer wrapper+inner-span (see awards.css) rather than by boxing
   its whole parent `<section>` — that section's banner-cover-fan and
   `.lore-grid` children already self-box independently, so boxing the
   section as well would double the gutter on them. */
.lore-container,
.lore-refine-tags,
.lore-header__inner,
.lore-story-head,
.lore-toc,
.lore-page-head,
.lore-genre-tiles,
.lore-profile-head,
.lore-continue-strip,
.lore-search-layout,
.lore-picks,
.lore-row,
.lore-grid,
.lore-tabs,
.lore-filterbar,
.lore-write-head,
.lore-story-manage-list,
.lore-stats-banner,
.lore-inbox,
.lore-coins-page,
.lore-promo-strips,
.lore-notif-list-wrap,
.lore-status-page,
.lore-awards-category__title {
	max-width: var(--max-w);
	margin-left: auto;
	margin-right: auto;
	padding-left: var(--gutter);
	padding-right: var(--gutter);
}

/* Buttons */
.lore-btn {
	display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
	min-height: 44px; padding: 0 var(--space-5);
	border-radius: var(--radius-pill); border: 1px solid transparent;
	font-weight: 600; font-size: 1rem; cursor: pointer;
	/* R5 (qa/r5/CSS_PARITY_R5.md, mobile-hero defect #2): inline-flex +
	   align-items:center vertically centres the LINE BOX, not the glyph's
	   full ink extent — with the browser's default `line-height:normal`
	   (~1.15-1.2 for most fonts) Bengali/Devanagari/Tamil conjuncts whose
	   matras stack below the baseline render taller than that line box and
	   get visually clipped at the top/bottom of the button. An explicit
	   generous line-height gives the glyph room without changing the
	   button's own height (still governed by min-height, not by this).
	   Fixed once here so every .lore-btn-based pill (CTA, tag pills that use
	   this class, .lore-btn--sm) inherits it — verified live in all four
	   `?lang=` spaces. */
	line-height: 1.6;
	transition: transform .1s ease, background-color .15s ease, border-color .15s ease, opacity .15s;
	background: var(--paper-alt); color: var(--ink);
}
.lore-btn:active { transform: scale(.98); }
.lore-btn--primary { background: var(--ember); color: #fff; }
.lore-btn--primary:hover { background: var(--ember-dark); }
.lore-btn--teal { background: var(--teal); color: #fff; }
.lore-btn--teal:hover { background: var(--teal-dark); }
.lore-btn--ghost { background: transparent; border-color: var(--line); color: var(--ink); }
.lore-btn--ghost:hover { background: var(--paper-alt); }
.lore-btn--danger { background: transparent; border-color: var(--danger); color: var(--danger); }
.lore-btn--sm { min-height: 36px; padding: 0 var(--space-4); font-size: .85rem; }
@media (max-width: 899px) {
	/* R3 tap-target fix (audit B6 + tap-targets §5): every .lore-btn--sm
	   sitewide (header auth CTAs, "Follow" buttons, chip-style toggles) was
	   30-36px tall on touch widths, well under the 44px minimum. One rule
	   here instead of patching each call site.
	   R4: was max-width:768px, which left it unfixed at 769-899px — a real
	   gap, since the harness (and the site's own documented breakpoint
	   scale two lines up) treats <900px as touch/mobile chrome (the tab bar
	   is visible up to 899px; desktop nav + header search start at 900px).
	   Measured live at 834px and 844px-landscape: "Log in"/"Sign up" both
	   still 36px tall. Matches the sitewide 900px chrome switch instead of
	   inventing a second breakpoint. */
	.lore-btn--sm { min-height: 44px; }
}
.lore-btn--block { width: 100%; }
.lore-btn[disabled], .lore-btn.is-loading { opacity: .6; pointer-events: none; }
/* R4 responsive audit (item 10): was 40px, under the 44px floor. */
.lore-btn--icon { min-height: 44px; min-width: 44px; padding: 0; border-radius: 50%; }

.lore-icon-btn {
	display: inline-flex; align-items: center; justify-content: center;
	width: 44px; height: 44px; border-radius: 50%; position: relative;
	background: transparent; border: 0; cursor: pointer; color: var(--ink);
}
.lore-icon-btn:hover { background: var(--paper-alt); }
.lore-icon { width: 22px; height: 22px; }
.lore-icon--sm { width: 16px; height: 16px; }

/* Header coin balance chip (round 2 phase 2) — overrides .lore-icon-btn's
   fixed square sizing since this one also carries a number; min-height 44px
   keeps the touch target compliant on mobile. */
.lore-header__coins {
	width: auto; height: 44px; padding: 0 var(--space-3); border-radius: var(--radius-pill);
	gap: 6px; background: var(--paper-alt); color: var(--ink); font-weight: 700; font-size: .85rem;
}
.lore-header__coins .lore-icon { width: 18px; height: 18px; color: var(--ember); }
.lore-header__coins-count { line-height: 1; }

/* Round 2 audit E-12: at narrow mobile widths (<=420px), a logged-in
   header's full icon row (language + coins-with-number + library + bell +
   avatar) genuinely overflowed `.lore-header__inner` by ~15px — and once a
   flex row overflows, browsers stop honouring its trailing-edge padding
   entirely (a cross-browser quirk, confirmed live: neither the container's
   own padding nor an explicit padding-right on the actions block had any
   effect while overflowing), which is what actually caused the right-most
   control to sit flush against the screen edge with zero gutter. The coin
   chip's number is the single widest element in that row purely because it
   carries text — collapse it to an icon-only 44px button (matching every
   other header icon) here so the row fits with room to spare; the balance
   is still one tap away (aria-label already says "Coin balance — get more
   coins", and Coins & Wallet is also in the avatar menu). */
@media (max-width: 420px) {
	.lore-header__coins { width: 44px; padding: 0; gap: 0; }
	.lore-header__coins-count { display: none; }
}

/* Forms */
.lore-field { margin-bottom: var(--space-4); }
.lore-field label, .lore-label {
	display: block; font-weight: 600; font-size: .875rem; margin-bottom: var(--space-2);
}
.lore-input, .lore-textarea, .lore-select {
	width: 100%; min-height: 46px; padding: 10px 14px;
	border: 1px solid var(--line); border-radius: var(--radius-md);
	background: var(--white); color: var(--ink); font-size: 1rem; font-family: inherit;
}
.lore-textarea { min-height: 120px; resize: vertical; }
.lore-input:focus, .lore-textarea:focus, .lore-select:focus { border-color: var(--teal); }
.lore-field__hint { font-size: .8rem; color: var(--ink-faint); margin-top: var(--space-1); }
.lore-field__error { font-size: .8rem; color: var(--danger); margin-top: var(--space-1); }
.lore-field.has-error .lore-input, .lore-field.has-error .lore-textarea { border-color: var(--danger); }
/* R4 responsive audit (item 10): the raw <input> is 18x18 by design (native
   checkbox), but it's wrapped in this <label> (see page-login.php /
   page-signup.php), so the REAL tap target is the whole row — tapping the
   "Remember me" text toggles it too. min-height only grows that row to
   44px tall; align-items stays flex-start so the box still sits on the
   text's first line, not awkwardly centred in the new empty space below. */
.lore-checkbox { display: flex; align-items: flex-start; gap: var(--space-2); font-size: .9rem; min-height: 44px; }
.lore-checkbox input { margin-top: 3px; width: 18px; height: 18px; flex: none; }

.lore-toggle { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); padding: var(--space-2) 0; }
.lore-toggle__switch { position: relative; width: 44px; height: 44px; flex: none; }
.lore-toggle__switch input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.lore-toggle__track { position: absolute; top: 50%; left: 0; width: 44px; height: 26px; transform: translateY(-50%); background: var(--line); border-radius: var(--radius-pill); transition: background .15s; }
.lore-toggle__track::after { content: ''; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; border-radius: 50%; background: #fff; transition: transform .15s; box-shadow: var(--shadow-sm); }
.lore-toggle__switch input:checked + .lore-toggle__track { background: var(--ember); }
.lore-toggle__switch input:checked + .lore-toggle__track::after { transform: translateX(18px); }

/* Chips */
.lore-chip {
	display: inline-flex; align-items: center; gap: 4px;
	padding: 4px 12px; border-radius: var(--radius-pill);
	background: var(--paper-alt); font-size: .8rem; font-weight: 600; color: var(--ink-soft);
	border: 1px solid var(--line);
	/* R5: same Indic-descender line-height risk as .lore-btn (checked, this
	   tag/chip pill shares the fault) — same fix. */
	line-height: 1.6;
}
.lore-chip--ember { background: var(--ember-light); color: var(--ember-dark); border-color: transparent; }
.lore-chip--teal { background: rgba(27,153,139,.12); color: var(--teal-dark); border-color: transparent; }
.lore-chip button { background: none; border: 0; cursor: pointer; color: inherit; line-height: 0; }
a.lore-chip { text-decoration: none; min-height: 44px; padding-top: 6px; padding-bottom: 6px; }
a.lore-chip.is-active { background: var(--ember); color: #fff; border-color: transparent; }

/* Story Type chips (round 2 audit D-3 — create-story form) — a native radio
   group (so the browser's own "please select one" validation just works)
   styled to look like Wattpad's 4 selectable chips. */
.lore-chip-radio-group { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-2); }
.lore-chip-radio {
	position: relative; display: inline-flex; align-items: center;
	padding: 7px 16px; border-radius: var(--radius-pill); border: 1px solid var(--line);
	background: var(--paper-alt); font-size: .86rem; font-weight: 600; color: var(--ink-soft);
	cursor: pointer; transition: background .15s, color .15s, border-color .15s;
}
/* R4 responsive audit (item 10 / parent_overflow): no top/left/right/bottom
   were set alongside position:absolute — with those auto, the browser falls
   back to the input's hypothetical static-flow position instead of the
   containing block's corner, which measured live as offset ~17px right and
   overflowing its own chip label by 15px. inset:0 is the fix every other
   "invisible full-cover input" in this codebase already uses; this was the
   one missing it. */
.lore-chip-radio input {
	position: absolute; inset: 0; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer;
}
.lore-chip-radio input:checked ~ span { color: inherit; }
.lore-chip-radio:has(input:checked) { background: var(--ember); color: #fff; border-color: transparent; }
.lore-chip-radio:has(input:focus-visible) { outline: 2px solid var(--teal); outline-offset: 2px; }

/* "Refine by tag" chip row (round 2 task 3/4 — browse/genre/search) */
.lore-refine-tags { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; margin-bottom: var(--space-4); }
.lore-refine-tags__label { font-size: .8rem; font-weight: 600; color: var(--ink-faint); margin-right: 2px; }

.lore-badge {
	display: inline-flex; align-items: center; justify-content: center;
	min-width: 18px; height: 18px; padding: 0 4px; border-radius: 999px;
	background: var(--ember); color: #fff; font-size: .75rem; font-weight: 700;
	position: absolute; top: 4px; right: 4px;
}

.lore-visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); }

/* Utility */
.u-flex { display: flex; } .u-flex-col { display: flex; flex-direction: column; }
.u-items-center { align-items: center; } .u-justify-between { justify-content: space-between; }
.u-gap-2 { gap: var(--space-2); } .u-gap-3 { gap: var(--space-3); } .u-gap-4 { gap: var(--space-4); }
.u-mt-4 { margin-top: var(--space-4); } .u-mb-4 { margin-bottom: var(--space-4); }
.u-muted { color: var(--ink-faint); }
.u-center-text { text-align: center; }
.u-sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); }

/* No horizontal scroll safety at small widths */
html, body { max-width: 100%; }
