/*
 * Fontrova UI Fix - 1.3.1
 *
 * Two production bugs, fixed with CSS only. No scroll listeners, no JS.
 * Every override wins on specificity + load order; no `!important` is used.
 *
 *  BUG 1 - sticky panel zig-zags / freezes half-rendered during slow scroll.
 *  BUG 2 - font previews render as tofu boxes on Android.
 *
 * Sections:
 *   1. Sticky panel: stop the mid-scroll reflow            (bug 1)
 *   2. Sticky panel: stop the scroll-driven anchor move    (bug 1)
 *   3. Sticky containing block: overflow-x hidden -> clip  (bug 1)
 *   4. Mobile compositing artefacts around the panel       (bug 1)
 *   5. Bundled Unicode coverage (@font-face)               (bug 2)
 *   6. Result font stack                                   (bug 2)
 *   7. Result rendering + responsive guards                (bug 2)
 *   8. Hide the Username/Bio/Caption preset row            (requested)
 *   9. Tighten the gap left behind by the hidden row       (requested)
 */

/* ------------------------------------------------------------------ *
 * 1. Sticky panel - make `.is-stuck` cosmetic only.
 *
 * Root cause of bug 1. `.fr-panel` is a normal-flow `position: sticky`
 * element, so its box still occupies layout space. The core stylesheet
 * shrinks that box when the IntersectionObserver adds `.is-stuck`:
 * it hides `.fr-label`, `.fr-metarow` and `.fr-presets` and shrinks
 * `.fr-input`. Measured on fontrova.com/tiktok-fonts/ at 375px, the
 * panel goes 279.2px -> 151.4px, a 127.8px collapse.
 *
 * That collapse happens *while the user is scrolling*:
 *   - the document shrinks 128px, so everything below jumps up  -> the jump
 *   - the observer callback lands a frame after the scroll paint -> the
 *     half-rendered / ghost frame the user sees
 *   - the 1px `[data-fr-sentinel]` sits immediately above the panel, so the
 *     reflow can push it back into view, un-setting `.is-stuck`, restoring
 *     128px, re-hiding it -> the zig-zag, the "two forces fighting"
 *
 * Fix: keep `.is-stuck` for its border + shadow affordance (those cost no
 * layout) and restore identical geometry in both states, so the panel has
 * exactly one height at every scroll position and the loop cannot start.
 * Values below are the core stylesheet's own un-stuck values, restated at
 * higher specificity. (`.fr-presets` itself is removed from the layout
 * entirely in section 8 below, so there is nothing to restore for it here.
 * `.fr-inputwrap`'s margin is restored to 0, not the core stylesheet's
 * 8px, because section 9 below sets the *normal*-state margin to 0 too -
 * both states must match, or this reintroduces the exact reflow this
 * section exists to remove.)
 * ------------------------------------------------------------------ */

.fr .fr-panel.is-stuck .fr-label {
	display: block;
}

.fr .fr-panel.is-stuck .fr-metarow {
	display: flex;
}

.fr .fr-panel.is-stuck .fr-input {
	min-block-size: 3.25rem;
	padding-block: var(--fr-sp-3);
	font-size: var(--fr-fs-md);
}

.fr .fr-panel.is-stuck .fr-inputwrap {
	margin-block-end: 0;
}

.fr .fr-panel.is-stuck .fr-inputwrap .fr-clear {
	inset-block-start: 1.85rem;
}

/* ------------------------------------------------------------------ *
 * 2. Sticky panel - freeze the sticky anchor.
 *
 * Second half of bug 1. `.fr-panel` resolves its top edge from
 * `inset-block-start: var(--fr-sticky-offset, 0px)`, and fontrova.js
 * rewrites `--fr-sticky-offset` on <html> from a scroll listener
 * (measureStickyOffset, debounced 100ms + rAF). Any theme header that
 * shows/hides on scroll therefore moves the panel's anchor mid-scroll,
 * one or two frames behind the scroll itself.
 *
 * Declaring the variable on `.fr` puts it closer in the inheritance
 * chain than <html>, so the panel reads this fixed value and the script's
 * writes become inert - native `position: sticky; top: 0` and nothing
 * else. The scroll listener still runs but now computes the same value
 * forever, so it stops writing after its first pass.
 *
 * fontrova.com's own header is `position: relative`, so the effective
 * offset here has always been 0. Sites that later add a fixed header
 * should override this one value rather than re-enable the script:
 *   .fr { --fr-sticky-offset: 64px; }
 * ------------------------------------------------------------------ */

.fr {
	--fr-sticky-offset: 0px;
}

/* ------------------------------------------------------------------ *
 * 3. Sticky containing block.
 *
 * The Astra theme sets `body { overflow-x: hidden }` (main.min.css).
 * `hidden` makes the body a scroll container in WebKit, which re-parents
 * every descendant `position: sticky` to a box that never scrolls - the
 * panel then sticks late, halfway, or not at all on iOS Safari.
 *
 * `clip` suppresses horizontal overflow identically but does NOT create a
 * scroll container, so sticky resolves against the viewport again.
 * Browsers without `overflow: clip` simply ignore this line and keep the
 * theme's `hidden`, so there is no regression risk. `html body` is used
 * to outrank the theme's `body` rule without `!important`.
 * ------------------------------------------------------------------ */

html body {
	overflow-x: clip;
}

/* ------------------------------------------------------------------ *
 * 4. Mobile compositing artefacts around the sticky panel.
 * ------------------------------------------------------------------ */

/* `mask-image` on the chips row promotes it to its own composited layer.
 * Inside a sticky ancestor on Chrome Android that layer is a known source
 * of stale-tile / ghost repaints during slow scroll. The mask is a purely
 * decorative right-edge fade, so drop it on touch devices only; pointer
 * devices keep it. */
@media (pointer: coarse) {
	.fr .fr-chips.is-scrollable {
		-webkit-mask-image: none;
		mask-image: none;
	}
}

/* Swiping the horizontal chips row must not chain into the page scroll
 * (or the browser's back-swipe) once it reaches an end. */
.fr .fr-chips {
	overscroll-behavior-x: contain;
}

/* `:hover` latches after a tap on touch devices, leaving cards visibly
 * offset by the hover transform. Suppress the transform where hover is
 * not a real input mode; the border/shadow feedback is unchanged. */
@media (hover: none) {
	.fr .fr-card:hover,
	.fr .fr-card:focus-within {
		transform: none;
	}
}

/* ------------------------------------------------------------------ *
 * 5. Bundled Unicode coverage - the actual fix for bug 2.
 *
 * The core stylesheet's result font stack is entirely *local* fonts:
 * "Segoe UI Symbol", "Noto Sans Symbols 2", "Noto Sans Math",
 * "Apple Symbols" … and there is not a single @font-face on the page for
 * result text (verified live: the only @font-face rules present belong to
 * Elementor's eicons and Font Awesome).
 *
 * Windows ships Segoe UI Symbol and macOS/iOS ships Apple Symbols, so the
 * previews render there. Android ships none of those families, and Roboto
 * has no coverage of U+1D400-1D7FF (Mathematical Alphanumeric Symbols) -
 * what every bold / italic / cursive / fraktur / double-struck / monospace
 * / sans style is built from. With nothing in the stack able to supply the
 * glyph, Chrome Android draws tofu boxes. Styles built from emoji instead
 * (Fire Name, Cursive Love, Bold Clan Tag) were unaffected, which matches
 * the reported screenshots exactly.
 *
 * Scope: fontrova.js ships its own glyph-support probe (probeGlyphs) and
 * risk classifier (bucketFor) that already name the exact styles capable of
 * rendering unsupported glyphs: math (every serif/sans/script/fraktur/
 * double-struck/monospace style), small caps, circled, squared, Cherokee,
 * Runic, Canadian Aboriginal Syllabics, Tifinagh, Ethiopic, Old Italic and
 * Gothic script lookalikes. The @font-face rules below cover precisely the
 * codepoints those specific map tables in this Fontrova install can emit -
 * read directly out of data/compiled-maps.php, not guessed from Unicode
 * block boundaries - so nothing is bundled that the tool can't produce, and
 * nothing the tool can produce is left uncovered. 'regional_indicator' is
 * deliberately excluded: flags are meant to keep using the system emoji
 * font, matching the plugin's own probe design.
 *
 * Sources: Noto Sans Math, Noto Sans Symbols, Noto Sans Symbols 2, Noto
 * Sans (for the handful of borrowed Latin/IPA glyphs a couple of lookalike
 * maps use), Noto Sans Cherokee, Noto Sans Runic, Noto Sans Canadian
 * Aboriginal, Noto Sans Tifinagh, Noto Sans Ethiopic, Noto Sans Old Italic
 * and Noto Sans Gothic - all SIL OFL 1.1 (see readme.txt). Each subset with
 * harfbuzz to only its needed codepoints: 122 KB total across 14 files.
 *
 * Every face carries a `unicode-range`, so fallback stays per-character: a
 * device that already has the glyph locally (Windows, iOS) requests 0
 * bytes from any of these; Android requests only the faces it actually
 * needs. `font-display: swap` keeps text paint immediate either way.
 * ------------------------------------------------------------------ */

/* Mathematical Alphanumeric Symbols + the Letterlike Symbols this map's own "hole" overrides substitute (ℎ, ℬ, ℯ, ℊ, ℋ, …). Covers every serif/sans/script/fraktur/double-struck/monospace style. */
@font-face {
	font-family: "Fontrova Math";
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("../fonts/fr-math.woff2") format("woff2");
	unicode-range: U+2102, U+210A-210E, U+2110-2112, U+2115, U+2119-211D, U+2124, U+2128, U+212C-212D, U+212F-2131, U+2133-2134, U+1D400-1D454, U+1D456-1D49C, U+1D49E-1D49F, U+1D4A2, U+1D4A5-1D4A6, U+1D4A9-1D4AC, U+1D4AE-1D4B9, U+1D4BB, U+1D4BD-1D4C3, U+1D4C5-1D505, U+1D507-1D50A, U+1D50D-1D514, U+1D516-1D51C, U+1D51E-1D539, U+1D53B-1D53E, U+1D540-1D544, U+1D546, U+1D54A-1D550, U+1D552-1D6A3, U+1D7CE-1D7FF;
}

/* Circled Latin + circled digits/numbers. Covers Circled and Circled (negative) styles. */
@font-face {
	font-family: "Fontrova Circled";
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("../fonts/fr-circled.woff2") format("woff2");
	unicode-range: U+2460-2468, U+24B6-24EA, U+24FF, U+2776-277E, U+1F150-1F169;
}

/* Squared Latin. Covers Squared and Squared (negative) styles. */
@font-face {
	font-family: "Fontrova Squared";
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("../fonts/fr-squared.woff2") format("woff2");
	unicode-range: U+1F130-1F149, U+1F170-1F189;
}

/* Small Capital Letters, drawn from IPA Extensions, Phonetic Extensions and Latin Extended-C - exactly the codepoints the Small Caps map emits. */
@font-face {
	font-family: "Fontrova SmallCaps";
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("../fonts/fr-smallcaps.woff2") format("woff2");
	unicode-range: U+262, U+26A, U+274, U+280, U+28F, U+299, U+29C, U+29F, U+1D00, U+1D04-1D05, U+1D07, U+1D0A-1D0B, U+1D0D, U+1D0F, U+1D18, U+1D1B-1D1C, U+1D20-1D22, U+A730-A731, U+A7AF;
}

/* Cherokee lookalikes. */
@font-face {
	font-family: "Fontrova Cherokee";
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("../fonts/fr-cherokee.woff2") format("woff2");
	unicode-range: U+13A0-13A2, U+13A9-13AC, U+13B0, U+13B3, U+13B7, U+13BB, U+13BE, U+13C0-13C1, U+13C3, U+13C6, U+13CC, U+13D2, U+13D5, U+13D9-13DA, U+13DE-13DF, U+13E2, U+13E6, U+13F4;
}

/* Runic lookalikes. */
@font-face {
	font-family: "Fontrova Runic";
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("../fonts/fr-runic.woff2") format("woff2");
	unicode-range: U+16A0-16A2, U+16A8, U+16B1-16B2, U+16B7, U+16B9-16BA, U+16BE, U+16C1, U+16C3, U+16C8-16CA, U+16CF, U+16D2, U+16D6-16D7, U+16DA, U+16DE-16DF, U+16E2, U+16E6, U+16EA;
}

/* Unified Canadian Aboriginal Syllabics lookalikes (plus two single-letter substitutes the map itself borrows from Latin Extended-A and Runic). */
@font-face {
	font-family: "Fontrova Syllabics";
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("../fonts/fr-syllabics.woff2") format("woff2");
	unicode-range: U+142F, U+144C, U+144E, U+1455, U+146B, U+146D, U+148D, U+14AA, U+14F0, U+1515, U+157C, U+1587, U+15B4, U+15BB, U+15DD, U+15E9-15EA, U+15EF-15F0, U+15F4, U+15F7, U+1614, U+161C, U+166D;
}
@font-face {
	font-family: "Fontrova Syllabics";
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("../fonts/fr-syllabics-1.woff2") format("woff2");
	unicode-range: U+166;
}
@font-face {
	font-family: "Fontrova Syllabics";
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("../fonts/fr-syllabics-2.woff2") format("woff2");
	unicode-range: U+16D5;
}

/* Tifinagh lookalikes. */
@font-face {
	font-family: "Fontrova Tifinagh";
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("../fonts/fr-tifinagh.woff2") format("woff2");
	unicode-range: U+2D30-2D31, U+2D33, U+2D36-2D37, U+2D3B-2D3D, U+2D3F-2D40, U+2D49, U+2D4D-2D4F, U+2D53-2D56, U+2D59-2D5C, U+2D5F, U+2D61-2D63;
}

/* Ethiopic lookalikes (plus one single-letter substitute the map borrows from IPA Extensions). */
@font-face {
	font-family: "Fontrova Ethiopic";
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("../fonts/fr-ethiopic.woff2") format("woff2");
	unicode-range: U+1206, U+120D, U+121D, U+1225, U+1229, U+122D, U+1246, U+1258, U+1268, U+1273, U+1275, U+1295, U+12A0, U+12A2, U+12AD, U+12CD, U+12D8, U+12E0, U+12ED, U+12F0, U+1301, U+130D, U+134D, U+1350, U+1354;
}
@font-face {
	font-family: "Fontrova Ethiopic";
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("../fonts/fr-ethiopic-1.woff2") format("woff2");
	unicode-range: U+253;
}

/* Old Italic lookalikes. */
@font-face {
	font-family: "Fontrova OldItalic";
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("../fonts/fr-olditalic.woff2") format("woff2");
	unicode-range: U+10300-10319;
}

/* Gothic script lookalikes. */
@font-face {
	font-family: "Fontrova Gothic";
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url("../fonts/fr-gothic.woff2") format("woff2");
	unicode-range: U+10330-10349;
}

/* ------------------------------------------------------------------ *
 * 6. Result font stack.
 *
 * Same value the core stylesheet sets, with the bundled families inserted
 * after the local symbol fonts and before the emoji fonts. Local first
 * means no download where the OS already has the glyph; bundled next means
 * no device is left with tofu. Declared on `.fr`, the same element the
 * core rule targets, so it wins purely on load order.
 *
 * "Noto Sans Symbols2" (no space) is Android's own spelling of the family
 * and is added alongside the spaced name the core stack already lists.
 * ------------------------------------------------------------------ */

.fr {
	--fr-font-result:
		"Segoe UI Symbol",
		"Noto Sans Symbols 2",
		"Noto Sans Symbols2",
		"Noto Sans Math",
		"Apple Symbols",
		"Fontrova Math",
		"Fontrova Circled",
		"Fontrova Squared",
		"Fontrova SmallCaps",
		"Fontrova Cherokee",
		"Fontrova Runic",
		"Fontrova Syllabics",
		"Fontrova Tifinagh",
		"Fontrova Ethiopic",
		"Fontrova OldItalic",
		"Fontrova Gothic",
		"Noto Color Emoji",
		"Segoe UI Emoji",
		var(--fr-font-ui);
}

/* ------------------------------------------------------------------ *
 * 7. Result rendering + responsive guards.
 * ------------------------------------------------------------------ */

/* `content-visibility: auto` lets Chrome Android skip rendering off-screen
 * cards and substitute the `contain-intrinsic-size` guess instead. During
 * a fast flick that shows up as blank / invisible cards, and because the
 * guess rarely matches the real height it also feeds scroll anchoring
 * corrections - i.e. it contributes to bug 1 as well as bug 2. The card
 * list is already batch-rendered by fontrova.js (BATCH + load-more), so
 * the DOM stays bounded without it. */
.fr .fr-card {
	content-visibility: visible;
	contain-intrinsic-size: none;
}

/* Many styles stack combining marks above the baseline (Cursive Dotted,
 * Bold Cursive Strike, Zalgo-style overlays). At line-height 1.35 those
 * marks sit outside the line box and are sheared off by the `overflow:
 * hidden` that `-webkit-line-clamp` requires. 1.5 clears them. */
.fr .fr-out {
	line-height: 1.5;
}

/* Long unbroken output must wrap inside the card rather than widen it.
 * Grid and flex children default to `min-width: auto`, which is what lets
 * an over-wide child push a row past the viewport; pin it to 0. */
.fr .fr-outwrap,
.fr .fr-cardfoot,
.fr .fr-out,
.fr .fr-grid {
	min-inline-size: 0;
	max-inline-size: 100%;
	overflow-wrap: anywhere;
}

/* Nothing inside the tool may create page-level horizontal scroll. */
.fr,
.fr .fr-results,
.fr .fr-card {
	max-inline-size: 100%;
	min-inline-size: 0;
}

/* At 320px the card's inner width leaves roughly 250px for output +
 * actions. The core's container query already stacks the action row below
 * 340px; this keeps the copy button from forcing a wider row before that
 * query applies. */
@media (max-width: 359px) {
	.fr .fr-copy {
		min-inline-size: 0;
		padding-inline: var(--fr-sp-3);
	}
}

/* ------------------------------------------------------------------ *
 * 8. Hide the Username / Bio / Caption preset row.
 *
 * Requested removal, not a bug fix. Clicking one of these buttons
 * (`.fr-preset`, `data-fr-preset="Username|Bio|Caption"`) only writes its
 * own label into the textarea when fontrova.js's `charLimit === 1` - a
 * sentinel the core script uses for tools with no configured character
 * limit. Confirmed live on fontrova.com/tiktok-fonts/ (`data-fr-charlimit`
 * is 80 there): clicking "Bio" leaves the textarea empty and only moves
 * focus into it. Every tool that sets a character limit is in the same
 * state, so on those pages the row does nothing but take up space.
 * `display: none` here is intentional and final, not a stand-in for a
 * real fix - there is no broken behavior left to mask, only an inert
 * control being removed by request.
 * ------------------------------------------------------------------ */

.fr .fr-presets {
	display: none;
}

/* ------------------------------------------------------------------ *
 * 9. Tighten the gap left behind by the hidden preset row.
 *
 * Requested spacing tweak, iterated twice against the live page:
 *
 *   - `.fr-inputwrap` carries `margin-block-end: var(--fr-sp-2)` (8px),
 *     sized to sit above the character-counter row.
 *   - `.fr-metarow` (the counter row) carries `margin-block-end:
 *     var(--fr-sp-3)` (12px), sized to sit above the (now-hidden, taller)
 *     preset row.
 *
 * Zeroing `.fr-inputwrap`'s margin and giving `.fr-metarow` a small 6px
 * one (rather than zeroing both) leaves a touch of breathing room below
 * the counter row without reopening the original 38px gap. Measured live
 * on fontrova.com/tiktok-fonts/: textarea-to-filter-row gap went
 * 98px (preset row still shown) -> 38px (section 8 alone) -> 18px (both
 * zeroed) -> 24px (this rule, by request - "thoda halka neeche"). The
 * counter text ("0 / 80 characters") still renders on its own line at
 * full size throughout - this is a tight fit, not an overlap.
 * ------------------------------------------------------------------ */

.fr .fr-inputwrap {
	margin-block-end: 0;
}

.fr .fr-metarow {
	margin-block-end: 6px;
}
