/*
 * Unistone accessibility remediation — stylesheet.
 * Every rule is tagged with the defect it closes. No design changes beyond what the defect requires.
 */

/* -----------------------------------------------------------------------------
 * UC-10 — horizontal scrolling at 320px (WCAG 1.4.10 AA)
 *
 * The register names only /ipo-offer-docs/. Measured at 320px, eleven of the thirteen pages
 * overflowed, and almost all of them for the same reason: the Unistone logo (attachment 1302,
 * intrinsic 1024×199) renders at full width inside the Elementor header and footer templates,
 * pushing documentElement.scrollWidth to 1034px. Nothing constrains it below the breakpoint.
 *
 * The header and footer are the widest scope that needs this; a blanket `img { max-width: 100% }`
 * would also override the inline `img { max-width: 2.5em }` this site sets on its icon images.
 *
 * /ipo-offer-docs/ adds a second offender: #pageSearch is 320px wide plus padding, so its border
 * box lands at 330px.
 * -------------------------------------------------------------------------- */

/*
 * SPECIFICITY MATTERS MORE THAN THE DECLARATIONS HERE — read before editing.
 *
 * Written first as `#ust-main img, ...`, which is an ID selector: specificity (1,0,1). Elementor
 * sizes individual images from its per-post stylesheet with
 * `.elementor-690 .elementor-element.elementor-element-1f86b6c img{height:280px}` — three classes,
 * (0,3,1). An ID beats any number of classes, so our `height:auto` silently won everywhere and
 * every deliberately-sized image on the site reverted to its intrinsic height: leadership portraits
 * 280px -> 539px, the "what we do" photos 200px -> 260px, and the client-logo ticker 70px -> 123px,
 * which is why a 2560px logo file filled the homepage. Reported by the client on 2026-07-29 and
 * found by comparing rendered image sizes against the pre-deploy backup.
 *
 * `:where()` contributes ZERO specificity, so this becomes (0,0,1) — exactly the weight Astra and
 * Elementor already give `img{max-width:100%;height:auto}` in their own base stylesheets. It still
 * catches images nothing else sizes (the 1024px header/footer logo below 320px, which is what this
 * rule was added for), and it can no longer outrank a rule that was written on purpose.
 *
 * Never reintroduce an ID or a long class chain here. Overriding the site's own design was never
 * the goal; not overflowing the viewport was.
 */
:where(#ust-main, .elementor-location-header, .elementor-location-footer) img {
	max-width: 100%;
	height: auto;
}

/*
 * The image rule alone does nothing here. Elementor wraps the logo in a bare inline-block <a>,
 * which has no width of its own: it shrinks to fit its child. So `max-width: 100%` on the image
 * resolves against a containing block that is itself sized by the image — a circular constraint
 * that clamps nothing, and the pair render at the intrinsic 1024px inside a 176px column.
 *
 * Capping the anchor breaks the circle. `:has()` would be the direct way to select it, but it is
 * avoided here in favour of the widget class, which needs no modern-selector support.
 */
.elementor-widget-image a {
	display: inline-block;
	max-width: 100%;
}

#pageSearch {
	max-width: 100%;
	box-sizing: border-box;
}

/*
 * 1.4.4 Resize Text — long unbreakable strings widen the page when text is enlarged.
 *
 * Browser zoom scales the viewport along with the text, so this never showed up in the May 2026
 * 200%-zoom pass. TEXT-ONLY enlargement does not scale the viewport, and then any token that
 * cannot be broken sets a floor under how narrow its container can get. At 320px and 200% text,
 * /investors-grievances/ ran 249px past the viewport edge and /annual-returns/ 166px.
 *
 * Three separate addresses were responsible, and finding all three took three passes:
 *   1. `compliance@unistonecapital.com` in the page body
 *   2. `mb@unistonecapital.com` in an Elementor icon box — inside a flex item, which is why it
 *      resisted the first fix: a flex item will not shrink below its content's min-content width
 *   3. the SAME icon box again in the FOOTER, which is outside #ust-main entirely. Both failing
 *      pages overflowed by an identical 59px, and an identical number on two different pages is
 *      what gave the shared chrome away.
 *
 * `anywhere`, NOT `break-word`. They look interchangeable and are not: only `anywhere` reduces the
 * intrinsic min-content size, which is precisely what lets the flex item above shrink. Substituting
 * `break-word` here measurably reverts case 2.
 *
 * Scope matches the image rule above — main plus header plus footer — and uses :where() so it
 * carries zero specificity and cannot outrank the site's own styling. Verified 0px overflow at
 * 140%, 180% and 200% on both pages, with the home page unchanged.
 */
:where(#ust-main, .elementor-location-header, .elementor-location-footer, footer.elementor)
	:is(a, p, li, td, th, span, div) {
	overflow-wrap: anywhere;
}

/* -----------------------------------------------------------------------------
 * UC-12 — in-text link distinguished from surrounding text by colour only (WCAG 1.4.1 A)
 *
 * Scoped to prose inside the main landmark so that buttons, nav items and card links — which are
 * distinguishable by shape and position — keep their existing treatment.
 * -------------------------------------------------------------------------- */

#ust-main p a:not(.elementor-button):not([class*="elementor-icon"]),
#ust-main li a:not(.elementor-button):not([class*="elementor-icon"]):not(.menu-link) {
	text-decoration: underline;
	text-underline-offset: 0.15em;
}

/* -----------------------------------------------------------------------------
 * UC-21 — required fields not visually indicated (WCAG 3.3.2 A)
 *
 * Elementor already sets aria-required="true" on the control and marks the wrapping group with
 * .elementor-field-required, so assistive technology is informed and only the visual cue is
 * missing. A CSS marker needs no markup change; the legend is injected by the PHP filter.
 * -------------------------------------------------------------------------- */

.elementor-field-group.elementor-field-required > label.elementor-field-label::after {
	content: " *";
	color: #b3261e;
	font-weight: 700;
}

.ust-required-legend {
	margin: 0 0 0.75em;
	font-size: 0.9em;
}

.ust-required-legend .ust-asterisk {
	color: #b3261e;
	font-weight: 700;
}

/* -----------------------------------------------------------------------------
 * UC-11 — scrollable content region not keyboard-focusable (WCAG 2.1.1 A)
 *
 * The region is given tabindex="0" by the PHP filter. A focus ring must be visible when a
 * keyboard user lands on it, otherwise the fix is invisible to the person it is for.
 * -------------------------------------------------------------------------- */

[data-ust-scroll-region]:focus-visible,
.ust-search-form :focus-visible,
.ust-carousel-toggle:focus-visible {
	outline: 3px solid #005fcc;
	outline-offset: 2px;
}

/* -----------------------------------------------------------------------------
 * UC-17 — carousel auto-rotates with no pause control (WCAG 2.2.2 A)
 * -------------------------------------------------------------------------- */

.ust-carousel-toggle {
	display: inline-flex;
	/*
	 * Elementor's entrance animation holds the carousel widget at `visibility: hidden` until it
	 * scrolls into view. The button is inserted as a sibling before that widget, so it should never
	 * inherit the hidden state — but `visibility` is inherited, and a pause control that cannot be
	 * focused is not a pause control. Re-assert it here so no future wrapper can take it away.
	 */
	visibility: visible;
	align-items: center;
	gap: 0.4em;
	margin: 0 0 0.75em;
	padding: 0.4em 0.9em;
	font: inherit;
	font-size: 0.85em;
	line-height: 1.4;
	color: #1a1a1a;
	background: #f2f2f2;
	border: 1px solid #767676;
	border-radius: 4px;
	cursor: pointer;
}

.ust-carousel-toggle:hover {
	background: #e4e4e4;
}

/*
 * `display: inline-flex` is not enough. The button is inserted as a sibling inside an Elementor
 * flex container (`e-con e-flex`), whose default `align-items: stretch` overrode the intrinsic
 * width and rendered a 1380px grey bar across the homepage (found by eye, 2026-07-29 — no
 * automated check looks at geometry). Element-qualified so it outranks Elementor's own
 * `.e-con > *` sizing rules.
 */
button.ust-carousel-toggle {
	align-self: flex-start;
	flex: 0 0 auto;
	width: fit-content;
	max-width: 100%;
}

/* -----------------------------------------------------------------------------
 * WCAG 2.2.2 — the single page-level motion control (see the matching comment in a11y.js).
 *
 * Sid's call, 2026-07-30: one control for all auto-moving content, not one button per carousel.
 * It is inserted as the FIRST child of #ust-main, so it precedes every element it governs and a
 * keyboard user meets it before any motion.
 *
 * `.ust-video-toggle` used to live here, absolutely positioned as a corner chip over the hero,
 * back when each video grew its own button. Nothing carries that class now and the rule is gone
 * rather than left to rot — a dead selector is how a stylesheet starts lying about the markup.
 *
 * Deliberately in normal flow, and deliberately NOT stretched: `button.ust-carousel-toggle`
 * above already pins `width:auto` + `align-self:flex-start`, which is what stops Elementor's
 * `align-items: stretch` turning this into the ~1380px grey bar that shipped on 29 July. The
 * margin below is the only addition — it keeps the control off the first section's top edge
 * without disturbing the page's own spacing rhythm.
 * -------------------------------------------------------------------------- */

.ust-motion-toggle {
	margin: 0.75em 0 0 0.75em;
}

/* The pinned variant, used when the control could be anchored to an already-positioned section
 * (the homepage hero). This is the fix for a regression shipped in v0.7.9's first cut: placed in
 * normal flow at the top of #ust-main, the button rendered UNDERNEATH the site's logo, because the
 * header's children are painted over the content area rather than occupying space above it. Every
 * gate passed — PHP lint, node --check, and a DOM assertion confirming the button existed and was
 * 248px wide. None of them can see overlap. It took a screenshot, which is the whole lesson of the
 * 29 July incident repeated.
 *
 * Pinned to the section's bottom-left, so it sits in the hero's clear lower area, far below the
 * branding. z-index clears Elementor's own background-video and overlay layers (which sit at
 * auto/1 inside the container) without competing with the header's stacking context. margin is
 * reset to 0 because the flow margin above would otherwise offset an absolute box. */

.ust-motion-toggle--pinned {
	position: absolute;
	inset-block-end: 1.25em;
	inset-inline-end: 1.25em;   /* right, not left: it sat over the hero headline and CTA */
	inset-inline-start: auto;
	z-index: 3;
	margin: 0;
	/* Smaller than the base chip. This is a secondary control on a marketing hero, so it should be
	 * findable without competing with the headline. Kept well above the 24x24 CSS-px floor that
	 * 2.5.8 Target Size (Minimum) asks for — measured after this change, not assumed. */
	font-size: 0.78rem;
	padding: 0.45em 0.85em;
	line-height: 1.35;
}

/* -----------------------------------------------------------------------------
 * SC 1.4.3 Contrast (Minimum), AA — /disclaimer/ only. Found 2026-07-30.
 *
 * The header's nav links are white, which works on every other page because a dark hero sits
 * behind them. /disclaimer/ has no hero, because the page has essentially no content at all — its
 * <main> holds 10 characters. So the nav renders WHITE ON WHITE. Measured in-browser with the WCAG
 * formula against the computed colours actually painted: ratio 1.00 on nine nav links. Not "low
 * contrast" — invisible. This is the only page of the 13 where it happens; every other page has
 * substantial content and therefore a hero.
 *
 * WHY TEXT COLOUR AND NOT A DARK BAR. A backdrop would fix the ratio, but it would also stamp a
 * dark band across the header, and the moment the client adds real content (with a hero) that band
 * would sit over their design for no reason. Dark text on a white page is simply correct, and it
 * degrades gracefully: if a hero does appear here later, the theme's own transparent-header rule
 * reasserts white on the pages that have one and only this page keeps the dark variant.
 *
 * Scoped by page id, which is fragile if the page is ever recreated — hence this comment. The
 * durable fix is the client publishing a disclaimer, which is a content matter and reported as one.
 *
 * ON `!important`, WHICH IS DELIBERATE HERE AND NOT LAZINESS. The first attempt used `:where()` to
 * keep specificity at zero, on the reasoning that lost us a homepage on 29 July — where an ID
 * selector accidentally outranked Elementor's per-image sizing and reverted every deliberately-sized
 * image on the site. That instinct was right in general and wrong here: measured in-browser, the
 * winning rule is Elementor's generated
 * `.elementor-11 .elementor-element.elementor-element-cc58746 .elementor-nav-menu--main .elementor-item`
 * at five classes (0,5,0), against (0,2,2) for the `:where()` version. It simply lost, and the nav
 * stayed invisible.
 *
 * The alternative to `!important` is copying that generated selector and adding a class to outrank
 * it — which hardcodes an Elementor element id (`elementor-element-cc58746`) that changes whenever
 * someone edits the header, and fails silently when it does. One `!important` on ONE property, on
 * ONE page, on ONE element type is the more predictable of the two, and its blast radius is
 * knowable by reading it. The lesson from 29 July is "know what you are outranking and how widely",
 * not "never outrank anything".
 *
 * Hover/focus is unaffected: that state paints a white pill and switches the text to the dark brand
 * colour anyway (measured 14.63:1), so this matches rather than fights it.
 * -------------------------------------------------------------------------- */

body.page-id-5455 .elementor-location-header a.elementor-item {
	color: #2b224c !important;
	/* Second half of the fix, 2026-07-30. The colour change above was necessary and insufficient:
	 * this page's header carries a vertical GRADIENT (dark purple at the top shading to near-white
	 * by y=80), and pixel measurement put the glyph-centre contrast at 4.26:1 - still under the
	 * 4.5:1 threshold, with the top half of every letter worse (2.14:1 at the top of the text box).
	 * No single text colour can pass against both ends of that gradient.
	 *
	 * So the links get an opaque backdrop instead: the same white pill the theme itself paints on
	 * hover/focus (its ::before treatment), which measures 14.63:1 with this text colour. Default
	 * state now matches the hover state's visual language on the one page that needs it. */
	background: #ffffff;
	border-radius: 8px;
}

/* 1.4.4 guard, added before this shipped rather than after. Measured at 200% TEXT-ONLY enlargement
 * the label grows this control from 249px to 419px — wider than a 320px viewport, which is exactly
 * the failure mode that put three email addresses past the viewport edge on /investors-grievances/
 * and /annual-returns/ earlier today. Browser zoom scales the viewport with the text and hides
 * this; text-only enlargement does not.
 *
 * `max-width` keeps it inside its container at any text size, and the wrap properties let the label
 * break instead of setting a min-content floor under the container. `overflow-wrap: anywhere` is
 * deliberate over `break-word`: only `anywhere` reduces min-content size, which is the property
 * that actually stops a flex or absolutely-positioned box from widening its parent. */

.ust-motion-toggle {
	max-width: calc(100% - 2.5em);
	overflow-wrap: anywhere;
	text-align: start;
}

/* -----------------------------------------------------------------------------
 * UC-24 — no second navigation mechanism; the [ivory-search] shortcode renders literally
 *         because the plugin that owned it is not installed (WCAG 2.4.5 AA)
 * -------------------------------------------------------------------------- */

.ust-search-form {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5em;
	align-items: flex-end;
}

.ust-search-form label {
	display: block;
	width: 100%;
	margin-bottom: 0.25em;
}

.ust-search-form input[type="search"] {
	flex: 1 1 12em;
	min-width: 0;
	padding: 0.5em 0.6em;
	border: 1px solid #767676;
	border-radius: 4px;
	font: inherit;
}

.ust-search-form button {
	padding: 0.55em 1.1em;
	font: inherit;
	color: #fff;
	background: #1a1a1a;
	border: 1px solid #1a1a1a;
	border-radius: 4px;
	cursor: pointer;
}

/* Respect a reduced-motion preference: the carousel is stopped by JS, and nothing else moves. */
@media (prefers-reduced-motion: reduce) {
	.ust-carousel-toggle {
		scroll-behavior: auto;
	}
}

/* -----------------------------------------------------------------------------
 * UC-01 — the investor-complaints table republished as accessible HTML
 *
 * The table has 8 columns and 55 rows. At 320px it cannot reflow without becoming unreadable, so
 * it scrolls inside its own container rather than pushing the page sideways (which would reopen
 * UC-10). The container is focusable and named, so a keyboard user can reach and scroll it — that
 * is the same fix UC-11 required for the disclaimer region.
 * -------------------------------------------------------------------------- */

.ust-grievance {
	margin: 2em 0;
}

.ust-table-scroll {
	overflow-x: auto;
	max-width: 100%;
	-webkit-overflow-scrolling: touch;
	/*
	 * `overflow-x: auto` alone does NOT stop this table from widening the document. Measured at
	 * 320px: the container is 320 wide and does scroll (clientWidth 320, scrollWidth 882), no
	 * element outside it crosses the viewport edge — and documentElement.scrollWidth is still 712.
	 * The table's painted overflow reaches the ancestors' scroll width regardless of the clip.
	 *
	 * `contain: paint` is the only thing that fixed it; min-width:0 on #ust-main, on
	 * #content.site-content, on the table itself, and `contain: inline-size` on the section were all
	 * tried and all left it at 712. Without this the page reopens UC-10 on /investors-grievances/.
	 */
	contain: paint;
}

.ust-table-scroll:focus-visible {
	outline: 3px solid #005fcc;
	outline-offset: 2px;
}

.ust-table {
	border-collapse: collapse;
	width: 100%;
	min-width: 44em;
	font-size: 0.95em;
}

.ust-table caption {
	text-align: left;
	font-weight: 700;
	padding: 0 0 0.5em;
}

.ust-table th,
.ust-table td {
	border: 1px solid #767676;
	padding: 0.4em 0.6em;
	text-align: left;
	vertical-align: top;
	/*
	 * Astra styles table cells #67768e, which lands at about 3.3:1 on the #f2f2f2 header fill —
	 * below the 4.5:1 that 1.4.3 requires. axe caught this on all 8 column headers. Set it here
	 * rather than inherit: this table is ours, and it must not depend on the theme's palette.
	 */
	color: #1a1a1a;
}

.ust-table thead th {
	background: #f2f2f2;
	color: #1a1a1a;
	font-weight: 700;
}

.ust-table tbody th[scope="row"] {
	white-space: nowrap;
	font-weight: 700;
}

/*
 * Links inside the table -- the "†" footnote marker and the per-row PDF links -- sit next to plain
 * text and would otherwise be distinguished by colour alone (WCAG 1.4.1, and axe's
 * link-in-text-block). UC-12's rule only reaches <p> and <li>, so table cells need their own.
 */
.ust-table a {
	text-decoration: underline;
	text-underline-offset: 0.15em;
}

#ust-grievance-note {
	margin-top: 0.75em;
	font-size: 0.9em;
}

/* -----------------------------------------------------------------------------
 * UC-14 — the MGT-7 annual-return HTML equivalent (data/mgt7.php).
 *
 * Added 2026-07-28. The page shipped with no styles at all in the first build: no
 * ust-mgt7-* rule existed here, so 29 label/value pairs rendered run-together as
 * "(DD/MM/YYYY)31/03/2025" — two adjacent spans with no whitespace between them.
 * Legible to a screen reader, unreadable to everyone else. On a statutory filing
 * that is a fidelity problem, not a cosmetic one.
 *
 * Deliberately restrained: this inherits Astra's type and colour and only supplies
 * structure, so it cannot fight the theme or drift when Astra updates.
 * -------------------------------------------------------------------------- */

/* The disclosure line that now fronts the collapsed MGT-7 block: same width as the block itself
 * so summary and content align, and the summary reads as a control rather than stray text. */
details.ust-mgt7-details {
	max-width: 1140px;
	margin: 1.5em auto;
	padding: 0 20px;
	box-sizing: border-box;
}
details.ust-mgt7-details > summary {
	cursor: pointer;
	font-weight: 600;
	padding: 0.6em 0.2em;
}

.ust-mgt7 {
	margin: 2em 0;
}

.ust-mgt7 > h2 {
	margin-bottom: 0.6em;
}

.ust-mgt7-note {
	border-left: 4px solid currentColor;
	padding: 0.75em 1em;
	margin: 0 0 1.75em;
	font-size: 0.95em;
}

.ust-mgt7-note p:last-child {
	margin-bottom: 0;
}

.ust-mgt7 h3 {
	margin: 2em 0 0.5em;
	font-size: 1.15em;
	line-height: 1.3;
}

.ust-mgt7-num {
	display: inline-block;
	min-width: 2.4em;
}

/* label/value pair — the run-together fix. flex + gap, never adjacent inline spans. */
.ust-mgt7-field {
	display: flex;
	flex-wrap: wrap;
	gap: 0.35em 1.25em;
	padding: 0.45em 0;
	border-bottom: 1px solid rgba(128, 128, 128, 0.25);
}

.ust-mgt7-label {
	flex: 1 1 22em;
	min-width: 0;
}

.ust-mgt7-value {
	flex: 0 1 auto;
	font-weight: 600;
	overflow-wrap: anywhere;
}

/* a selected radio/checkbox, stated in words */
.ust-mgt7-choice {
	margin: 1em 0;
	padding: 0.6em 0.9em;
	border-left: 3px solid currentColor;
}

.ust-mgt7-q {
	margin: 0 0 0.3em;
}

.ust-mgt7-a {
	margin: 0;
}

.ust-mgt7-opts {
	font-size: 0.9em;
	/*
	 * NO opacity here. It was `opacity: 0.75` for one build and axe returned 12
	 * color-contrast violations on this page — a genuine WCAG 1.4.3 failure introduced
	 * by an accessibility remediation, in CSS added to fix a different defect. Dimming
	 * text is never free; it is a contrast change wearing a styling costume.
	 */
}


/* Containment for the injected MGT-7 block, added 2026-07-30. The block has rendered full-bleed —
 * text hard against the viewport edge — since it shipped: it is appended after Elementor's own
 * containers close, so no page container ever wrapped it. Every stored screenshot back to v0.7.10
 * shows the same, so this is original presentation, not a regression; it simply took someone
 * scrolling down and LOOKING (Sid, tonight) for it to be seen. Width matches Elementor's boxed
 * content default so the block reads as part of the page rather than an accident. */
.ust-mgt7 {
	max-width: 1140px;
	margin-left: auto;
	margin-right: auto;
	padding-left: 20px;
	padding-right: 20px;
	box-sizing: border-box;
}

.ust-mgt7-table {
	border-collapse: collapse;
	width: 100%;
	min-width: 32em;
	font-size: 0.92em;
}

.ust-mgt7-table th,
.ust-mgt7-table td {
	border: 1px solid rgba(128, 128, 128, 0.4);
	padding: 0.4em 0.6em;
	text-align: left;
	vertical-align: top;
}

.ust-mgt7-table thead th {
	font-weight: 700;
}

.ust-mgt7-foot {
	margin-top: 2em;
	font-size: 0.9em;   /* see .ust-mgt7-opts — no opacity, same reason */
}

/* -----------------------------------------------------------------------------
 * UC-09 — the collapsed video transcript (v0.7.1).
 * Inline transcript exploded the homepage media carousel (2026-07-29); see the
 * injection code in unistone-a11y.php for the full account.
 * -------------------------------------------------------------------------- */

.ust-transcript {
	margin: 0.75em 0 0;
	text-align: left;
}

/*
 * The summary carries its OWN colours rather than inheriting.
 *
 * Inheriting looked right in the editor and failed on the page: the corporate video sits in a dark
 * navy section, and the theme's inherited body colour rgb(103,118,142) against rgb(15,16,33) is
 * 4.08:1 — under the 4.5:1 that 18px/600 needs, so the control we added to help people read the
 * narration was itself hard to read. Found by looking at the rendered page on 2026-07-29; axe did
 * not raise it, because the element is injected client-side after the scan.
 *
 * Same chip treatment as .ust-carousel-toggle: #1a1a1a on #f2f2f2 is ~15:1 and holds on any
 * background this ever lands on, light or dark, without knowing which.
 */
.ust-transcript > summary {
	cursor: pointer;
	display: inline-flex;
	align-items: center;
	gap: 0.4em;
	padding: 0.4em 0.9em;
	font-size: 0.9em;
	font-weight: 600;
	color: #1a1a1a;
	background: #f2f2f2;
	border: 1px solid #767676;
	border-radius: 4px;
}

.ust-transcript > summary:hover {
	background: #e4e4e4;
}

/*
 * The transcript body inherits the section's colour too, and has the same problem the moment it is
 * opened. Give the panel its own ground.
 */
.ust-transcript[open] > *:not(summary) {
	color: #1a1a1a;
	background: #fff;
	padding: 0.75em 1em;
	border: 1px solid #767676;
	border-top: 0;
	border-radius: 0 0 4px 4px;
}

.ust-transcript > summary:focus-visible {
	outline: 3px solid #005fcc;
	outline-offset: 2px;
}

.ust-transcript[open] {
	max-height: 24em;
	overflow-y: auto;
}
