/**
 * Attestra public styles.
 *
 * Deliberately restrained: this renders inside the customer's own theme, so it
 * inherits typography and only brings what it needs. Every rule is scoped to an
 * .att- class to avoid touching anything else on the page.
 */

.att-verify,
.att-result,
.att-list {
	--att-ink: #140c33;

	/* Everything below is mixed from the accent colour in PHP and handed over
	   as custom properties, so setting one colour in Settings re-tints the
	   whole form. The literals are only fallbacks for the case where that
	   inline block did not run. */
	--att-muted: var(--attestra-muted, #6b6590);
	--att-line: var(--attestra-line, #e4e0ff);
	--att-paper: var(--attestra-paper, #f7f6ff);
	--att-brand: var(--attestra-brand, #4a22e0);

	/* Chosen for contrast against the accent, not assumed to be white — a
	   pale accent needs dark text or the label disappears again. */
	--att-brand-ink: var(--attestra-brand-ink, #ffffff);
	--att-brand-hover: var(--attestra-brand-hover, #3a17bd);
	--att-green: #0f9d58;
	--att-amber: #d98300;
	--att-pink: #e5197f;

	/* Derived from the accent so the whole console re-tints from one setting.
	   Each has a flat fallback first for browsers without color-mix. */
	--att-tint: rgba(74, 34, 224, .07);
	--att-tint: color-mix(in srgb, var(--att-brand) 7%, transparent);
	--att-glow: rgba(74, 34, 224, .16);
	--att-glow: color-mix(in srgb, var(--att-brand) 16%, transparent);
	--att-beam: rgba(74, 34, 224, .10);
	--att-beam: color-mix(in srgb, var(--att-brand) 10%, transparent);
	--att-beam-line: rgba(74, 34, 224, .42);
	--att-beam-line: color-mix(in srgb, var(--att-brand) 42%, transparent);

	/* One shadow, used at two depths. Tinted with the ink rather than pure
	   black so it sits on a coloured page without turning grey. */
	--att-lift: 0 1px 2px rgba(20, 12, 51, .04), 0 14px 34px -16px rgba(20, 12, 51, .22);
	--att-lift-sm: 0 1px 2px rgba(20, 12, 51, .05), 0 6px 16px -10px rgba(20, 12, 51, .3);

	/* The one easing used everywhere: a firm start and a long settle, which
	   reads as something mechanical coming to rest rather than as bounce. */
	--att-ease: cubic-bezier(.22, 1, .36, 1);

	/* Credential IDs are codes read character by character off a printed
	   page, so they are set in a monospace face wherever they appear. */
	--att-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;

	box-sizing: border-box;
	color: var(--att-ink);
}

.att-verify *,
.att-result *,
.att-list * { box-sizing: border-box; }

/* ---------- Lookup form ---------- */

/*
 * The console.
 *
 * Everything to do with checking a credential lives on one card, so the two
 * routes in — typing the ID, handing over the file — read as one instrument
 * with two settings rather than as two separate forms stacked on a page.
 */
.att-verify {
	position: relative;
	max-width: 760px;
	margin: 0 auto;
	padding: 26px 26px 24px;
	border: 1px solid var(--att-line);
	border-radius: 18px;
	background: #fff;
	box-shadow: var(--att-lift);

	/*
	 * Clips the beam below, and stops the tab marker escaping the card.
	 *
	 * `clip` rather than `hidden` because `hidden` makes this a scroll
	 * container, and a scroll container will be scrolled: focusing any child
	 * wider than the card — a tab label on a narrow phone — made the browser
	 * scroll the console sideways to reveal it, shifting every child left and
	 * leaving the text clipped mid-word with no way to scroll back. `clip`
	 * clips without ever becoming scrollable. `hidden` first, for browsers
	 * that do not know `clip`; the script below covers them.
	 */
	overflow: hidden;
	overflow: clip;
	isolation: isolate;
}

/*
 * The rule across the top.
 *
 * Not a plain bar: it is ticked, at the pitch of the fine engraved bands
 * printed along the edge of a certificate or a banknote. That texture is the
 * one visual convention this whole product borrows from, and it does the same
 * job here as it does there — it says "official document" before a word has
 * been read. Held to 3px so it registers as a material, not as decoration.
 */
.att-verify::before {
	content: "";
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	height: 3px;
	background-color: var(--att-brand);
	background-image: repeating-linear-gradient(
		90deg,
		var(--att-brand-hover) 0 2px,
		transparent 2px 5px
	);
	z-index: 2;
}

/*
 * The scan beam — the signature of this design.
 *
 * A single bar of light travels down the console while a check is running. It
 * is doing the job a spinner would, but it says something a spinner cannot:
 * the document is being read. It fires for both routes in, which is what ties
 * the two tabs together into one instrument.
 */
.att-verify::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	top: -34%;
	height: 34%;
	z-index: 3;
	opacity: 0;
	pointer-events: none;
	background: linear-gradient(
		180deg,
		transparent,
		var(--att-beam) 44%,
		var(--att-beam-line) 50%,
		var(--att-beam) 56%,
		transparent
	);
}

.att-verify--scanning::after {
	animation: att-sweep 1.1s cubic-bezier(.45, .05, .3, 1) infinite;
}

@keyframes att-sweep {
	0%   { opacity: 0; transform: translateY(0); }
	12%  { opacity: 1; }
	88%  { opacity: 1; }
	100% { opacity: 0; transform: translateY(400%); }
}

.att-verify__heading { margin: 0 0 6px; font-size: 21px; letter-spacing: -.01em; }

/* An eyebrow rather than a sentence: this is a field on an official form, and
   it should look like one. */
.att-verify__label {
	display: block;
	margin-bottom: 8px;
	font-size: 11px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: .12em;
	color: var(--att-muted);
}

.att-verify__row { display: flex; gap: 10px; flex-wrap: wrap; }

.att-verify__input {
	flex: 1 1 240px;
	min-width: 0;
	padding: 15px 16px;
	border: 1px solid var(--att-line);
	border-radius: 12px;
	background: var(--att-paper);
	font-size: 16px; /* 16px stops iOS zooming the page on focus. */
	font-family: var(--att-mono);
	color: var(--att-ink);
	letter-spacing: .09em;
	transition: border-color .2s ease, background-color .2s ease, box-shadow .2s ease;
}

/* A surname is prose, not a code, so it keeps the theme's own face. */
.att-verify__input--name {
	flex: 0 1 190px;
	font-family: inherit;
	letter-spacing: normal;
}

.att-verify__input:focus {
	outline: none;
	border-color: var(--att-brand);
	background: #fff;
	box-shadow: 0 0 0 4px var(--att-glow);
}

.att-verify__button {
	position: relative;
	padding: 15px 28px;
	border: 1px solid var(--att-brand);
	border-radius: 12px;
	background-color: var(--att-brand);

	/* A light from above rather than a two-colour gradient: it works against
	   any accent the site owner picks, including a pale one. */
	background-image: linear-gradient(180deg, rgba(255, 255, 255, .17), rgba(255, 255, 255, 0) 62%);
	color: var(--att-brand-ink);
	font-size: 15px;
	font-weight: 650;
	font-family: inherit;
	letter-spacing: .01em;
	cursor: pointer;
	box-shadow: var(--att-lift-sm);
	transition: background-color .2s ease, transform .2s var(--att-ease), box-shadow .2s ease;
}

.att-verify__button:hover:not(:disabled) {
	background-color: var(--att-brand-hover);
	transform: translateY(-1px);
	box-shadow: var(--att-lift);
}

.att-verify__button:active:not(:disabled) {
	transform: translateY(1px);
	box-shadow: var(--att-lift-sm);
}

.att-verify__button:focus-visible {
	outline: 2px solid var(--att-brand);
	outline-offset: 3px;
}

.att-verify__button:disabled { opacity: .65; cursor: default; transform: none; }

.att-verify__hint {
	margin: 12px 0 0;
	font-size: 13px;
	line-height: 1.5;
	color: var(--att-muted);
}

/* ---------- Result ---------- */

.att-result {
	margin-top: 24px;
	border: 1px solid var(--att-line);
	border-radius: 16px;
	background: #fff;
	padding: 24px;
	border-top: 4px solid var(--att-muted);
	box-shadow: var(--att-lift-sm);

	/* The panel is replaced wholesale on every check, so the animation runs
	   again each time a new answer arrives — which is exactly when the eye
	   needs to be told something changed. */
	animation: att-result-in .5s var(--att-ease) both;
}

@keyframes att-result-in {
	from { opacity: 0; transform: translateY(12px) scale(.99); }
	to   { opacity: 1; transform: none; }
}

/*
 * Inside the console, the verdict is not a second card.
 *
 * The rules above give the result its own border, radius and shadow, which is
 * right when [attestra_record] prints one on its own on a page. Dropped into
 * the console it produced a white card sitting on a white card, two borders
 * and two shadows deep — the reader's eye has to work out whether it is
 * looking at one thing or two, and the answer is one.
 *
 * So here it becomes a panel of the instrument instead: tinted rather than
 * raised, and carrying the verdict colour along its leading edge, where it
 * runs the full height of the answer instead of capping it with a line.
 */
.att-verify .att-result {
	border: 0;
	border-left: 3px solid var(--att-status, var(--att-muted));
	border-radius: 0 14px 14px 0;
	box-shadow: none;
	background: var(--att-paper);
	padding: 22px 22px 22px 24px;
}

/*
 * Everything that was tinted to stand off a white card has to be re-tinted to
 * stand off a tinted panel, or it disappears. The ID chip took its background
 * from the same variable the panel now uses, and the row separators were two
 * points away from the paper they were drawn on.
 */
.att-verify .att-result .att-row code { background: #fff; }
.att-verify .att-result .att-row { border-bottom-color: var(--att-line); }
.att-verify .att-result .att-result__grid { border-top-color: var(--att-line); }
.att-verify .att-result .att-downloads { border-top-color: var(--att-line); }
.att-verify .att-result .att-cert img { background: #fff; }

/* One property carries the verdict colour, and the edge, the badge and the
   result's own tint all read it. Adding a status is now one line rather than
   three rules that can drift apart. */
.att-result--valid      { --att-status: var(--att-green); }
.att-result--expired    { --att-status: var(--att-amber); }
.att-result--revoked    { --att-status: var(--att-pink); }
.att-result--altered    { --att-status: var(--att-pink); }
.att-result--missing    { --att-status: var(--att-muted); }
.att-result--unreadable { --att-status: var(--att-amber); }

.att-result { border-top-color: var(--att-status, var(--att-muted)); }

.att-result__head { display: flex; gap: 14px; align-items: flex-start; }

.att-result__icon {
	flex: 0 0 auto;
	width: 34px;
	height: 34px;
	border-radius: 50%;
	background: var(--att-muted);
	position: relative;
	animation: att-pop .52s cubic-bezier(.34, 1.4, .64, 1) .06s both;
}

@keyframes att-pop {
	from { opacity: 0; transform: scale(.4); }
	to   { opacity: 1; transform: none; }
}

/*
 * Two rings leave the badge when a credential checks out, and only then.
 * A verifier reading this page is looking for one answer, and this is the
 * only place in the design that celebrates. Failure states stay still — an
 * animated flourish on "withdrawn" would be in poor taste.
 */
.att-result--valid .att-result__icon::before {
	content: "";
	position: absolute;
	inset: -5px;
	border-radius: 50%;
	border: 2px solid var(--att-green);
	opacity: 0;
	animation: att-halo 1.5s ease-out .3s 2;
}

@keyframes att-halo {
	0%   { opacity: .65; transform: scale(.85); }
	70%  { opacity: 0; transform: scale(1.45); }
	100% { opacity: 0; transform: scale(1.45); }
}

/* The tick and cross are drawn rather than loaded, so a result never waits
   on an icon font or an image request. */
.att-result__icon { background: var(--att-status, var(--att-muted)); }

.att-result--valid .att-result__icon::after {
	content: '';
	position: absolute;
	left: 12px;
	top: 8px;
	width: 8px;
	height: 15px;
	border: solid #fff;
	border-width: 0 2.5px 2.5px 0;
	transform: rotate(45deg);
}

/*
 * The cross was two full-width gradient bars rotated inside a circle and
 * clipped by it, which left four blunt arms of slightly unequal length. Two
 * strokes of a stated length, crossed, is the same drawing done properly —
 * and it is the shape a person recognises without having to look twice.
 */
.att-result--revoked .att-result__icon::after,
.att-result--altered .att-result__icon::after,
.att-result--missing .att-result__icon::after {
	content: '';
	position: absolute;
	left: 50%;
	top: 50%;
	width: 2.5px;
	height: 16px;
	margin: -8px 0 0 -1.25px;
	border-radius: 2px;
	background: #fff;
	box-shadow: 0 0 0 0 transparent;
	transform: rotate(45deg);
}

.att-result--revoked .att-result__icon::before,
.att-result--altered .att-result__icon::before,
.att-result--missing .att-result__icon::before {
	content: '';
	position: absolute;
	left: 50%;
	top: 50%;
	width: 2.5px;
	height: 16px;
	margin: -8px 0 0 -1.25px;
	border-radius: 2px;
	background: #fff;
	transform: rotate(-45deg);
}

.att-result--expired .att-result__icon::after {
	content: '';
	position: absolute;
	left: 15px;
	top: 8px;
	width: 2.5px;
	height: 10px;
	background: #fff;
	box-shadow: 0 12px 0 #fff;
}

.att-result__title { margin: 0; font-size: 20px; font-weight: 700; }
.att-result__message { margin: 4px 0 0; color: var(--att-muted); font-size: 14.5px; }

.att-result__grid {
	margin: 20px 0 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));

	/* A column gap, where there was none. Two values in adjacent columns were
	   separated only by whatever slack the longer one happened to leave, so a
	   long title ran to within a character of the value beside it. */
	column-gap: 28px;
	row-gap: 0;
	border-top: 1px solid var(--att-line);

	/* Rows sit on a shared baseline grid rather than each being as tall as its
	   own content, which is what keeps the separators lining up across the two
	   columns when one side wraps to three lines and the other to one. */
	align-items: stretch;
}

/*
 * A title is prose and the rest are labels.
 *
 * Everything in this grid is a short value except the title, which on an
 * inspection report is a full sentence — "Inspection Report of Thorough Visual
 * Examination of Temporary Tower Build Platform". Sharing a half-width column
 * with it, that wraps to three or four lines while the credential ID beside it
 * uses one, and the ID is left stranded at the top of a tall empty box with the
 * whole row knocked out of alignment.
 *
 * So the title takes the full width and the pairs below it stay paired. It also
 * reads in the right order: what the document is, and then its reference.
 */
.att-row--holder_name,
.att-row--award {
	grid-column: 1 / -1;
}

.att-row--holder_name dd {
	font-size: 16.5px;
	font-weight: 600;
	line-height: 1.35;
	max-width: 62ch; /* long titles stay readable rather than running the full width */
}

.att-row {
	padding: 12px 0;
	border-bottom: 1px solid #f3f1ff;

	/* The value sits at the top of its row and the separator at the bottom,
	   so two rows of different heights still meet on one line. */
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 3px;
}

.att-row dt {
	margin: 0;
	font-size: 11.5px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	color: var(--att-muted);
	line-height: 1.3;
}

/*
 * An explicit colour, not an inherited one.
 *
 * The labels have always named their own colour while the values took whatever
 * the theme gave them. That is fine until a theme styles `dd` — and a theme
 * that sets it to white, or to the same tint this panel is drawn on, makes
 * every value on the page invisible while every label stays put, which looks
 * like the data failed to load rather than like a colour problem.
 */
.att-row dd {
	margin: 0;
	font-size: 15px;
	line-height: 1.45;
	color: var(--att-ink);

	/* A credential ID has no spaces to break at, and neither do some equipment
	   serials. Without this they push their column wider than its share. */
	overflow-wrap: anywhere;
}

/*
 * The values are held visible against the theme.
 *
 * This panel renders inside somebody else's design, and a definition list is
 * a structure themes style freely — often to hide a `dd` outright, since most
 * themes only ever meet one in a widget or a comment list where the value is
 * decorative. On a verification result it is the entire point: a site was
 * found where every label appeared and every value beside it collapsed to
 * nothing, so a credential that had verified correctly, whose data was sitting
 * complete in the HTML, showed as a row of empty headings. Nothing announced
 * it, because from the plugin's side everything had worked.
 *
 * The labels never had this problem, which is what took so long to see: they
 * name their own font and colour, so a theme rule that emptied the values left
 * the headings looking perfectly healthy above them.
 *
 * Written against the properties a rule would actually use to hide something —
 * every one of these is a way an element can be in the document and take up no
 * space. Ordinary styling a theme applies, its font or its spacing, is left
 * alone; only disappearing is overridden. The declarations carry weight
 * because the rule being overridden usually does too.
 */
.att-result .att-row dt,
.att-result .att-row dd {
	display: block !important;
	visibility: visible !important;
	height: auto !important;
	max-height: none !important;
	min-height: 0;
	overflow: visible !important;
	opacity: 1 !important;
	position: static !important;
	clip: auto !important;
	clip-path: none !important;
	text-indent: 0 !important;
	transform: none !important;
}

/* Font size is separate: zeroing it is another way to hide text, but a theme
   may legitimately want a different size, so this sets a floor rather than a
   fixed value by restating the intended size at the same weight. */
.att-result .att-row dd { font-size: 15px !important; }
.att-result .att-row dt { font-size: 11.5px !important; }

/* The same face the ID was typed in. Someone checking a printed certificate
   compares the code on the page against the code in the answer character by
   character, and that comparison is easier when both are set identically. */
.att-row code {
	font-family: var(--att-mono);
	font-size: 13.5px;
	letter-spacing: .06em;
	background: var(--att-paper);
	border: 1px solid var(--att-line);
	padding: 3px 8px;
	border-radius: 6px;
}

.att-field-image { max-width: 130px; height: auto; border-radius: 6px; }

.att-result__assets {
	display: flex;
	gap: 18px;
	flex-wrap: wrap;
	align-items: flex-start;
	margin-top: 18px;
}

.att-cert { flex: 1 1 280px; margin: 0; }
.att-cert img { width: 100%; height: auto; border: 1px solid var(--att-line); border-radius: 10px; display: block; }

/* ------------------------------------------------------------------ */
/* Paging through a multi-page document                                */
/* ------------------------------------------------------------------ */

/* Only one page is visible at a time. The others stay in the document so
   the browser can lazy-load them and so the pager has nothing to fetch when
   it moves; `display: none` is what keeps them out of the layout without
   taking them out of the DOM.

   Scoped to the paged figure rather than written as a bare `.att-cert__page`,
   and not for tidiness: `.att-cert img` above sets `display: block` and, being
   a class and an element, outweighs a lone class. The bare rule lost the tie
   and every page of a multi-page report stacked down the screen, with the
   pager sitting uselessly underneath saying "Page 1 of 5". Two classes settle
   it. The rule is also now correct in a way it was not before — pages are
   hidden only where there is a pager to bring them back. */
.att-cert--paged .att-cert__page { display: none; }
.att-cert--paged .att-cert__page.is-current { display: block; }

/* The first page carries is-current from the server rather than waiting for
   the script to apply it, so a visitor with scripting off still sees the
   document — they simply see page one of it, with a pager that does nothing. */

.att-cert__pager {
	display: flex;
	/* Above the transparent layer that copy protection lays over the figure.
	   That layer exists to stop the image being the event target, and it was
	   written when a certificate was one image and nothing else; without this
	   it would swallow the clicks meant for the pager and the document would
	   be stuck on its first page whenever protection was on. */
	position: relative;
	z-index: 1;
	align-items: center;
	justify-content: center;
	gap: 12px;
	margin-top: 10px;
}

.att-cert__step {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	padding: 0;
	border: 1px solid var(--att-line);
	border-radius: 8px;
	background: #fff;
	color: var(--att-ink);
	font-size: 18px;
	line-height: 1;
	cursor: pointer;
}

.att-cert__step:hover:not(:disabled) {
	border-color: var(--att-brand);
	color: var(--att-brand);
}

/* Kept visible rather than hidden at the ends of the document. A control that
   disappears moves the ones beside it, and the page counter jumping sideways
   every time someone reaches the last page reads as a glitch. */
.att-cert__step:disabled {
	opacity: .4;
	cursor: default;
}

.att-cert__count {
	font-size: 13px;
	font-weight: 600;
	color: var(--att-muted);
	font-variant-numeric: tabular-nums;
}

/*
 * The pager is the only control on a result panel, and on a phone it is being
 * tapped by someone holding the certificate in the other hand. A 32px square
 * is a small target for that, so it grows on narrow screens and the whole
 * strip is given a surface, which also stops it reading as though it belongs
 * to whatever follows the result.
 */
@media (max-width: 600px) {
	.att-cert__pager {
		gap: 8px;
		padding: 8px 10px;
		margin-top: 12px;
		border: 1px solid var(--att-line);
		border-radius: 10px;
		background: #fff;
	}

	.att-cert__step {
		width: 44px;
		height: 44px;
		font-size: 20px;
	}

	.att-cert__count {
		flex: 1 1 auto;
		text-align: center;
		font-size: 14px;
	}
}

.att-qr { flex: 0 0 auto; margin: 0; text-align: center; }

.att-qr img {
	width: 120px;
	height: 120px;
	image-rendering: pixelated; /* keeps modules crisp when scaled */
	border: 1px solid var(--att-line);
	border-radius: 8px;
	display: block;
}

.att-qr figcaption { margin-top: 5px; font-size: 12px; color: var(--att-muted); }

/* ---------- Check chain ---------- */

.att-checks {
	margin: 20px 0 0;
	padding: 0;
	list-style: none;
	display: grid;
	gap: 7px;
}

/*
 * The four checks arrive one after another rather than all at once. They are
 * genuinely a sequence — the register is searched, then the signature, then
 * the revocation list, then the dates — so showing them in order is not
 * decoration, it is the truth about what happened, replayed at a speed a
 * person can follow.
 */
.att-check {
	position: relative;
	padding-left: 26px;
	font-size: 13.5px;
	color: var(--att-muted);
	animation: att-check-in .42s var(--att-ease) both;
}

.att-check:nth-child(1) { animation-delay: .16s; }
.att-check:nth-child(2) { animation-delay: .25s; }
.att-check:nth-child(3) { animation-delay: .34s; }
.att-check:nth-child(4) { animation-delay: .43s; }

@keyframes att-check-in {
	from { opacity: 0; transform: translateX(-7px); }
	to   { opacity: 1; transform: none; }
}

.att-check::before {
	content: '';
	position: absolute;
	left: 0;
	top: 4px;
	width: 14px;
	height: 14px;
	border-radius: 50%;
	background: #e6e3f7;
}

.att-check--pass::before { background: var(--att-green); }
.att-check--pass::after {
	content: '';
	position: absolute;
	left: 5px;
	top: 6px;
	width: 4px;
	height: 8px;
	border: solid #fff;
	border-width: 0 1.5px 1.5px 0;
	transform: rotate(45deg);
}

.att-check--fail::before { background: #d9d5ee; }

.att-result__meta { margin: 14px 0 0; font-size: 12.5px; color: var(--att-muted); }
.att-result__note { margin: 8px 0 0; font-size: 13px; color: var(--att-muted); }

/* ---------- Register table ---------- */

.att-list__search { display: flex; gap: 8px; margin-bottom: 14px; }

.att-list__search input {
	flex: 1;
	padding: 10px 13px;
	border: 1px solid var(--att-line);
	border-radius: 9px;
	font-size: 15px;
	font-family: inherit;
}

.att-list__search button {
	padding: 10px 18px;
	border: 1px solid var(--att-brand);
	border-radius: 9px;
	background: var(--att-brand);
	color: var(--att-brand-ink);
	font-weight: 600;
	font-family: inherit;
	cursor: pointer;
}

.att-table { width: 100%; border-collapse: collapse; font-size: 14px; }

.att-table th {
	text-align: left;
	padding: 10px 12px;
	border-bottom: 2px solid var(--att-line);
	font-size: 11.5px;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	color: var(--att-muted);
}

.att-table td { padding: 11px 12px; border-bottom: 1px solid #f3f1ff; }

.att-pill {
	display: inline-block;
	padding: 3px 9px;
	border-radius: 999px;
	font-size: 11.5px;
	font-weight: 700;
}

.att-pill--active  { background: #e4f6ec; color: #0b6b3d; }
.att-pill--expired { background: #fdf1dd; color: #8a5200; }
.att-pill--revoked { background: #fde8f1; color: #a30f49; }
.att-pill--draft   { background: #eeecfb; color: #4a4270; }

.att-empty { padding: 24px; text-align: center; color: var(--att-muted); }

/* Tables are the one thing that genuinely breaks on a phone, so the register
   scrolls sideways instead of squashing. */
@media (max-width: 640px) {
	.att-list { overflow-x: auto; }
	.att-table { min-width: 520px; }
	.att-result { padding: 18px 16px; }
	.att-result__grid { grid-template-columns: 1fr; }
}

/* ------------------------------------------------------------------ */
/* Tabs: enter an ID, or hand over the certificate                     */
/* ------------------------------------------------------------------ */

/*
 * A segmented control rather than an underline. The two routes in are
 * alternatives to each other, not sections of a document, and a segmented
 * control is the control that says "one of these two" — the same reason a
 * radio group is not a list of links.
 */
.att-tabs {
	position: relative;
	display: inline-flex;
	gap: 2px;
	margin: 0 0 20px;
	padding: 4px;
	border: 1px solid var(--att-line);
	border-radius: 999px;
	background: var(--att-paper);
	max-width: 100%;
}

.att-tab {
	position: relative;
	z-index: 1;
	appearance: none;
	background: none;
	border: 0;
	border-radius: 999px;
	padding: 9px 18px;
	font: inherit;
	font-size: 14px;
	font-weight: 600;
	color: var(--att-muted);
	cursor: pointer;
	white-space: nowrap;
	transition: color .28s var(--att-ease);
}

.att-tab:hover { color: var(--att-ink); }

/* Without script there is no marker to sit behind the label, so the active
   tab is coloured instead. The class below is added by the script that
   builds the marker, and only then does the label flip to the ink colour. */
.att-tab--on { color: var(--att-brand); }
.att-tabs--marked .att-tab--on { color: var(--att-brand-ink); }
.att-tabs--marked .att-tab--on:hover { color: var(--att-brand-ink); }

.att-tab:focus-visible {
	outline: 2px solid var(--att-brand);
	outline-offset: 2px;
}

/* The marker slides between segments instead of appearing under the new one.
   The travel is the whole point: it shows the two routes are the same
   control in two positions. */
.att-tabs__marker {
	position: absolute;
	z-index: 0;
	top: 4px;
	bottom: 4px;
	left: 0;
	width: var(--att-marker-w, 0);
	border-radius: 999px;
	background-color: var(--att-brand);
	background-image: linear-gradient(180deg, rgba(255, 255, 255, .16), rgba(255, 255, 255, 0) 62%);
	box-shadow: var(--att-lift-sm);
	transform: translateX(var(--att-marker-x, 0));
	transition: transform .42s var(--att-ease), width .42s var(--att-ease);
	pointer-events: none;
}

/* No transition on the very first paint, or the marker slides in from the
   left edge on page load as though something had just been clicked. */
.att-tabs__marker--placed { transition: none; }

/* Hidden rather than removed, so switching tabs keeps a chosen file. */
.att-panel-body--hidden { display: none; }

.att-panel-body { animation: att-panel-in .38s var(--att-ease) both; }

@keyframes att-panel-in {
	from { opacity: 0; transform: translateY(6px); }
	to   { opacity: 1; transform: none; }
}

/* ------------------------------------------------------------------ */
/* Upload area                                                         */
/* ------------------------------------------------------------------ */

.att-drop {
	position: relative;
	display: flex;
	align-items: center;
	gap: 16px;
	padding: 26px 22px;
	border: 2px dashed var(--att-line);
	border-radius: 14px;
	background: var(--att-paper);
	cursor: pointer;
	overflow: hidden;
	transition: border-color .22s ease, background-color .22s ease,
		transform .22s var(--att-ease), box-shadow .22s ease;
}

/* A wash of the accent that grows from the middle on hover. Cheaper than it
   looks: one transform on one layer, no repaint of the border. */
.att-drop::before {
	content: "";
	position: absolute;
	inset: 0;
	background: var(--att-tint);
	opacity: 0;
	transform: scale(.92);
	transition: opacity .28s ease, transform .4s var(--att-ease);
	pointer-events: none;
}

.att-drop:hover {
	border-color: var(--att-brand);
	transform: translateY(-2px);
	box-shadow: var(--att-lift-sm);
}

.att-drop:hover::before { opacity: 1; transform: scale(1); }

/* Dragging a file over it is the one moment the target should look live, so
   it breathes until the file is let go. */
.att-drop--over {
	border-color: var(--att-brand);
	background-color: #fff;
	transform: translateY(-2px);
	animation: att-breathe 1.4s ease-in-out infinite;
}

.att-drop--over::before { opacity: 1; transform: scale(1); }

@keyframes att-breathe {
	0%, 100% { box-shadow: 0 0 0 0 var(--att-glow); }
	50%      { box-shadow: 0 0 0 8px transparent; }
}

/* Solid once a file is held: the target has stopped asking for something and
   is now showing what it has. */
.att-drop--filled {
	border-style: solid;
	border-color: var(--att-brand);
	background-color: #fff;
}

.att-drop--filled::before { opacity: 1; transform: scale(1); }

/* The input stays in the DOM and keeps working — it is only moved out of
   sight, so the surrounding label remains a real file picker for anyone
   using a keyboard or a screen reader. */
.att-drop__input {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
	border: 0;
}

.att-drop__input:focus-visible + .att-drop__icon {
	outline: 2px solid var(--att-brand);
	outline-offset: 3px;
}

.att-drop__icon {
	flex: 0 0 auto;
	width: 44px;
	height: 44px;
	border-radius: 50%;
	background: var(--att-brand);
	position: relative;
	z-index: 1;
	box-shadow: var(--att-lift-sm);
	transition: transform .32s var(--att-ease), background-color .22s ease;
}

/* The arrow rises a little when the target is live — the gesture the control
   is asking for, performed small. */
.att-drop:hover .att-drop__icon,
.att-drop--over .att-drop__icon { transform: translateY(-3px); }

.att-drop--filled .att-drop__icon { transform: none; background: var(--att-green); }

/* An upward arrow drawn in CSS, so no image has to load for it. */
.att-drop__icon::before,
.att-drop__icon::after {
	content: "";
	position: absolute;
	background: #fff;
}

.att-drop__icon::before {
	width: 2px;
	height: 17px;
	top: 13px;
	left: 21px;
	transition: opacity .18s ease;
}

.att-drop__icon::after {
	width: 10px;
	height: 10px;
	top: 14px;
	left: 17px;
	background: none;
	border-left: 2px solid #fff;
	border-top: 2px solid #fff;
	transform: rotate(45deg);
}

/* The arrow becomes a tick once a file is held. Same circle, same two
   strokes, redrawn — so the control is visibly the same thing in a new
   state rather than a different badge swapped in. */
.att-drop--filled .att-drop__icon::before { opacity: 0; }

.att-drop--filled .att-drop__icon::after {
	width: 9px;
	height: 16px;
	top: 12px;
	left: 18px;
	border-left: 0;
	border-top: 0;
	border-right: 2.5px solid #fff;
	border-bottom: 2.5px solid #fff;

	/* Its own keyframe rather than the shared att-pop. That one ends on
	   `transform: none`, which with a `both` fill silently threw away the
	   45-degree rotation and left the tick drawn as an open corner. Any
	   keyframe touching transform has to restate the rotation. */
	animation: att-tick-pop .42s var(--att-ease) both;
}

@keyframes att-tick-pop {
	from { opacity: 0; transform: rotate(45deg) scale(.3); }
	to   { opacity: 1; transform: rotate(45deg) scale(1); }
}

.att-drop__body {
	display: flex;
	flex-direction: column;
	gap: 4px;
	position: relative;
	z-index: 1;

	/* A flex item will not shrink below the width of its longest word unless
	   it is told it may. Without this the note text held the row open and
	   pushed the file name onto a line of its own. */
	min-width: 0;
}

.att-drop__title { font-weight: 650; color: var(--att-ink); }

.att-drop__note { font-size: 13px; color: var(--att-muted); line-height: 1.45; }

/*
 * The file name always occupies exactly one line.
 *
 * It used to be capped at a third of the row and allowed to break anywhere,
 * which turned an ordinary name like certificate-ATT-2026-4417.pdf into a
 * three-line block of hyphenated fragments — the least readable possible
 * rendering of the one string in the control the reader wants to check.
 *
 * A name too long for the space is shortened through its middle in script,
 * because the informative end of a certificate file name is the end: the
 * credential ID and the extension. Trimming the tail, which is what a plain
 * ellipsis does, throws away precisely the part worth reading.
 */
.att-drop__file {
	/*
	 * How many characters of the name are shown. One number, declared here,
	 * that decides both how wide the pill may grow and where the script cuts
	 * the name.
	 *
	 * These started out as two separate numbers — a cap in the stylesheet and
	 * a limit in the script — and two numbers that have to agree will
	 * eventually disagree. They did, at narrow widths: the stylesheet trimmed
	 * a name the script had already trimmed, and the second cut took the file
	 * extension off the end. CSS owns the value because CSS owns the
	 * breakpoints; the script reads it back off the element instead of
	 * keeping a copy that can drift.
	 */
	--att-name-max: 29;

	position: relative;
	z-index: 1;
	margin-left: auto;
	min-width: 0;

	/* The row has three items and, at some widths, not enough space for all
	   three at their natural size. Something has to give, and it must not be
	   this one: the note beside it is prose that reads perfectly well over
	   two lines, whereas a squeezed file name gets trimmed by the ellipsis
	   below and loses its extension. So the name holds its size and the note
	   absorbs the shortfall. */
	flex: 0 0 auto;

	/* In characters rather than pixels: the content is monospace, so one ch
	   is exactly one character wide in whichever mono face the device
	   resolves, and no guess about font metrics is involved. The added pixels
	   are this element's own horizontal padding. */
	max-width: calc(var(--att-name-max) * 1ch + 26px);

	padding: 6px 12px;
	border-radius: 999px;
	background: var(--att-tint);
	font-family: var(--att-mono);
	font-size: 12.5px;
	font-weight: 600;
	line-height: 1.4;
	color: var(--att-brand);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

/* Empty most of the time, and an empty pill is a smudge. */
.att-drop__file:empty { display: none; }

.att-drop--filled .att-drop__file { animation: att-slide-in .38s var(--att-ease) both; }

@keyframes att-slide-in {
	from { opacity: 0; transform: translateX(10px); }
	to   { opacity: 1; transform: none; }
}

.att-verify__button--upload { margin-top: 18px; }

.att-result__matched {
	margin: 6px 0 0;
	font-size: 13px;
	color: var(--att-muted);
}

.att-result--unreadable { border-top-color: var(--att-amber); }

/* ------------------------------------------------------------------ */
/* Download buttons on a result                                        */
/* ------------------------------------------------------------------ */

.att-downloads {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 10px 14px;
	margin-top: 20px;
	padding-top: 18px;
	border-top: 1px solid var(--att-line);
}

.att-downloads__label {
	font-size: 13px;
	font-weight: 600;
	color: var(--att-muted);
}

.att-downloads__buttons { display: flex; flex-wrap: wrap; gap: 8px; }

.att-download {
	display: inline-block;
	padding: 7px 14px;
	border: 1px solid var(--att-line);
	border-radius: 8px;
	background: #fff;
	font-size: 13px;
	font-weight: 600;
	color: var(--att-ink);
	text-decoration: none;
}

.att-download:hover {
	border-color: var(--att-brand);
	color: var(--att-brand);
}

.att-download--pdf {
	background: var(--att-brand);
	border-color: var(--att-brand);
	color: var(--att-brand-ink);
}

.att-download--pdf:hover { color: var(--att-brand-ink); opacity: .9; }

@media (max-width: 640px) {
	.att-verify { padding: 20px 16px 18px; border-radius: 14px; }

	/* The field and the button stop sharing a row, so neither gets squeezed
	   to a width nobody can read a credential ID in. */
	.att-verify__input,
	.att-verify__input--name { flex: 1 1 100%; }
	.att-verify__button { width: 100%; }

	/* Full width so the two segments split the screen evenly rather than
	   huddling in the corner. */
	.att-tabs { display: flex; width: 100%; }

	/*
	 * The label may wrap here. A flex item will not shrink below the width of
	 * its content while that content is one unbreakable line, so `nowrap` on
	 * a phone made "Upload the certificate" hold the control wider than the
	 * card it sits in — which is what let focus scroll the console sideways.
	 * Two short lines in a segment is a far smaller cost than that.
	 */
	.att-tab {
		flex: 1 1 0;
		min-width: 0;
		padding: 8px 8px;
		font-size: 13.5px;
		line-height: 1.25;
		white-space: normal;
		text-align: center;
	}

	.att-drop { flex-wrap: wrap; padding: 20px 16px; gap: 12px; }

	/* Without a basis the body wraps below the icon, leaving a 44px circle
	   alone on a line looking like a bullet point. */
	.att-drop__body { flex: 1 1 60%; }

	/* Its own row rather than sharing one with the note. It sizes itself to
	   the character budget above; stretching it to the full width would only
	   put a long pale bar under a short name. */
	.att-drop__file { margin-left: 0; }

	.att-downloads__buttons { width: 100%; }
	.att-download { flex: 1 1 auto; text-align: center; }
}

/*
 * The narrowest phones still in use. At 320px, once the card and the drop
 * target have taken their padding, under 200px is left for the name — so the
 * budget comes down rather than the type size. A shorter name at a readable
 * size beats the whole name set too small to read.
 */
@media (max-width: 400px) {
	.att-drop__file { --att-name-max: 21; }
}

/* ------------------------------------------------------------------ */
/* Standing up to the theme                                            */
/* ------------------------------------------------------------------ */

/*
 * Everything above is written at single-class specificity, which is the polite
 * thing to do inside somebody else's theme. It is not enough for form controls.
 *
 * A shortcode renders inside the post content, and most themes style buttons
 * from there — `.entry-content button`, `.wp-block-post-content button`,
 * `.wp-element-button` and so on. Those selectors carry a class *and* an
 * element, which outranks a bare class, so the theme wins.
 *
 * When it wins on some properties and not others the result is worse than
 * either alone: a theme that resets the button background to white, while our
 * `color: #fff` survives, produces a button with its label written in white on
 * white. The button is there, the text is there, and the visitor sees an empty
 * outlined box with no idea what it does.
 *
 * So the rules that decide whether a control is legible are restated here
 * using the real markup chain, giving three classes plus an element. Two was
 * not enough: it only ties with patterns like `.site .entry-content button`,
 * and a tie goes to whichever stylesheet loads last, which is normally the
 * theme. Colour and background are always set together — a half-applied pair
 * is what causes the invisible-label failure in the first place.
 *
 * No !important. These are defaults worth defending, not decisions worth
 * forcing, and a site owner who deliberately restyles Attestra should still be
 * able to.
 */

.att-verify:not(.att-verify--theme) .att-verify__row button.att-verify__button,
.att-verify:not(.att-verify--theme) .att-verify__upload button.att-verify__button,
.att-list:not(.att-list--theme) .att-list__search button[type="submit"] {
	background-color: var(--att-brand);
	background-image: linear-gradient(180deg, rgba(255, 255, 255, .17), rgba(255, 255, 255, 0) 62%);
	color: var(--att-brand-ink);
	border: 1px solid var(--att-brand);
	border-radius: 12px;
	padding: 15px 28px;
	font-size: 15px;
	font-weight: 650;
	font-family: inherit;
	line-height: 1.3;
	text-shadow: none;
	text-transform: none;
	letter-spacing: .01em;
	cursor: pointer;
}

.att-verify:not(.att-verify--theme) .att-verify__row button.att-verify__button:hover:not(:disabled),
.att-verify:not(.att-verify--theme) .att-verify__row button.att-verify__button:focus:not(:disabled),
.att-verify:not(.att-verify--theme) .att-verify__upload button.att-verify__button:hover:not(:disabled),
.att-verify:not(.att-verify--theme) .att-verify__upload button.att-verify__button:focus:not(:disabled) {
	background: var(--att-brand-hover);
	color: var(--att-brand-ink);
}

.att-verify:not(.att-verify--theme) .att-verify__row button.att-verify__button:disabled,
.att-verify:not(.att-verify--theme) .att-verify__upload button.att-verify__button:disabled {
	background: var(--att-brand);
	color: var(--att-brand-ink);
	opacity: .65;
	cursor: default;
}

.att-verify .att-verify__row input.att-verify__input {
	background: var(--att-paper);
	background-image: none;
	color: var(--att-ink);
	border: 1px solid var(--att-line);
	border-radius: 12px;
	padding: 15px 16px;
	font-family: var(--att-mono);
	letter-spacing: .09em;

	/* The screenshot that prompted this showed the placeholder rendered as
	   "E.G. ATT-2026-4417" — the theme uppercases inputs. Harmless on the
	   placeholder, misleading on a credential ID, where someone checking a
	   printed certificate needs to see exactly what they typed. */
	text-transform: none;
}

.att-verify .att-verify__row input.att-verify__input--name {
	font-family: inherit;
	letter-spacing: normal;
}

.att-verify .att-verify__row input.att-verify__input:focus {
	background: #fff;
	border-color: var(--att-brand);
	box-shadow: 0 0 0 4px var(--att-glow);
}

/*
 * The rule above did not fix it on the site that reported it, because the
 * theme sets the transform on the placeholder pseudo-element rather than on
 * the input — a different element, so a rule about the input never reaches
 * it however specific it is. It has to be said to the placeholder directly.
 *
 * Not grouped into one selector: an unrecognised pseudo-element invalidates
 * the whole selector list it appears in, and these are vendor-prefixed, so
 * grouping them means the browser that understands one throws away the rest.
 */
.att-verify .att-verify__row input.att-verify__input::placeholder {
	text-transform: none;
	letter-spacing: .06em;
	opacity: .7;
	color: var(--att-muted);
}

.att-verify .att-verify__row input.att-verify__input::-webkit-input-placeholder {
	text-transform: none;
	letter-spacing: .06em;
	color: var(--att-muted);
}

.att-verify .att-verify__row input.att-verify__input::-moz-placeholder {
	text-transform: none;
	letter-spacing: .06em;
	opacity: .7;
	color: var(--att-muted);
}

.att-verify .att-tabs button.att-tab {
	background: none;
	background-image: none;
	border: 0;
	border-radius: 999px;
	padding: 9px 18px;
	color: var(--att-muted);
	font-family: inherit;
	font-size: 14px;
	font-weight: 600;
	text-transform: none;
	letter-spacing: normal;
	box-shadow: none;
	text-shadow: none;
}

.att-verify .att-tabs button.att-tab--on {
	background: none;
	color: var(--att-brand);
}

.att-verify .att-tabs--marked button.att-tab--on,
.att-verify .att-tabs--marked button.att-tab--on:hover {
	background: none;
	color: var(--att-brand-ink);
}

.att-result .att-downloads a.att-download {
	background: #fff;
	background-image: none;
	color: var(--att-ink);
	border: 1px solid var(--att-line);
	text-decoration: none;
}

.att-result .att-downloads a.att-download:hover {
	border-color: var(--att-brand);
	color: var(--att-brand);
}

.att-result .att-downloads a.att-download--pdf {
	background: var(--att-brand);
	border-color: var(--att-brand);
	color: var(--att-brand-ink);
}

.att-result .att-downloads a.att-download--pdf:hover {
	background: var(--att-brand-hover);
	color: var(--att-brand-ink);
}

.att-verify .att-verify__upload label.att-drop {
	background: var(--att-paper);
	color: var(--att-ink);
}

.att-verify .att-verify__upload label.att-drop .att-drop__title { color: var(--att-ink); }
.att-verify .att-verify__upload label.att-drop .att-drop__note  { color: var(--att-muted); }

/* ------------------------------------------------------------------ */
/* Last resort: legibility                                             */
/* ------------------------------------------------------------------ */

/*
 * The layer above raises specificity, which is the correct fix and handles
 * most themes. It does not handle a theme that resets buttons with
 * !important, and several popular ones do — usually as a blanket
 * `button { background: transparent !important; }` in a normalise sheet.
 *
 * When that happens the result is not "styled differently". Our white label
 * colour survives while the background is stripped, so the button becomes an
 * empty outlined box: the label is there, the click works, and the visitor has
 * no idea what it does. That is a broken control, not a styling disagreement.
 *
 * So exactly two properties are forced, and only on our own buttons: the
 * background and the text colour that sit on top of each other. They are
 * forced as a pair, because forcing one and not the other is what produced the
 * problem in the first place.
 *
 * Everything else — size, spacing, radius, typography — is left alone at
 * normal weight, so a theme can still restyle these buttons to match the site.
 * A site owner who wants a different colour can still set one; they just have
 * to be as specific as this rule, which is a fair price for a button that is
 * guaranteed to be readable out of the box.
 */

.att-verify:not(.att-verify--theme) .att-verify__row button.att-verify__button,
.att-verify:not(.att-verify--theme) .att-verify__upload button.att-verify__button,
.att-list:not(.att-list--theme) .att-list__search button[type="submit"] {
	background-color: var(--att-brand) !important;

	/* Forced too, because a theme's blanket `background: none !important`
	   would otherwise take the gradient and leave the flat colour, which is
	   the same half-applied pair this section exists to prevent. */
	background-image: linear-gradient(180deg, rgba(255, 255, 255, .17), rgba(255, 255, 255, 0) 62%) !important;
	color: var(--att-brand-ink) !important;
	border-color: var(--att-brand) !important;
	opacity: 1;
}

.att-verify:not(.att-verify--theme) .att-verify__row button.att-verify__button:hover:not(:disabled),
.att-verify:not(.att-verify--theme) .att-verify__upload button.att-verify__button:hover:not(:disabled) {
	background-color: var(--att-brand-hover) !important;
	color: var(--att-brand-ink) !important;
}

.att-verify:not(.att-verify--theme) .att-verify__row button.att-verify__button:disabled,
.att-verify:not(.att-verify--theme) .att-verify__upload button.att-verify__button:disabled {
	opacity: .65;
}

.att-verify .att-tabs button.att-tab {
	background-color: transparent !important;
	background-image: none !important;
	color: var(--att-muted) !important;
}

.att-verify .att-tabs button.att-tab--on {
	color: var(--att-brand) !important;
}

/* The marker is a separate element behind the label, so the segment itself
   stays transparent — only the label colour has to change to stay readable
   once the marker has slid underneath it. */
.att-verify .att-tabs--marked button.att-tab--on {
	color: var(--att-brand-ink) !important;
}

.att-result .att-downloads a.att-download {
	background-color: #fff !important;
	background-image: none !important;
	color: var(--att-ink) !important;
}

.att-result .att-downloads a.att-download--pdf {
	background-color: var(--att-brand) !important;
	color: var(--att-brand-ink) !important;
}

/*
 * Opting out entirely.
 *
 * When "Inherit my theme's button styling" is chosen, the wrapper carries
 * .att-verify--theme and every selector above stops matching. That is the
 * whole mechanism, and it is deliberately all-or-nothing: we either set the
 * background and the text colour together, or we set neither and let the theme
 * own both. Setting one and leaving the other is what made a button with an
 * unreadable label in the first place, so there is no middle setting to pick.
 */

/* ------------------------------------------------------------------ */
/* Protected certificates                                              */
/* ------------------------------------------------------------------ */

/*
 * Discourages casual copying only. The image is still bytes in the browser,
 * and a screenshot defeats all of this. What actually protects the document is
 * that this image is a reduced-resolution copy served through PHP, with the
 * print-resolution original not reachable by any address.
 */

.attestra-protected,
.attestra-protected * {
	-webkit-user-select: none;
	-moz-user-select: none;
	-ms-user-select: none;
	user-select: none;
	-webkit-touch-callout: none;
}

/* The credential ID stays selectable. Someone verifying a certificate very
   often needs to copy the ID into an email or a record of their own, and
   taking that away punishes the honest use of the page to inconvenience the
   dishonest one. */
.attestra-protected code,
.attestra-protected .att-result__message {
	-webkit-user-select: text;
	-moz-user-select: text;
	-ms-user-select: text;
	user-select: text;
}

.att-cert--protected {
	position: relative;
}

/* A transparent layer over the image. Right-click and drag are already
   blocked in script; this stops the image itself being the event target at
   all, which closes the "open image in new tab" route that some browsers
   offer without going through the context menu. */
.att-cert--protected::after {
	content: "";
	position: absolute;
	inset: 0;
	background: transparent;
}

.att-cert--protected img {
	pointer-events: none;
}

/* ------------------------------------------------------------------ */
/* Reduced motion                                                      */
/* ------------------------------------------------------------------ */

/*
 * Not a blanket `animation: none`. Removing the animation from something
 * whose start frame is `opacity: 0` leaves it invisible, and `both` fill
 * modes here mean several elements would simply never appear.
 *
 * So everything still runs, and still ends where it ended before — the
 * movement is taken out and the duration collapsed, which leaves a plain
 * fade. Nothing travels, nothing repeats, and nothing loops forever.
 */
@media (prefers-reduced-motion: reduce) {
	.att-verify,
	.att-verify * {
		animation-duration: .01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: .01ms !important;
		scroll-behavior: auto !important;
	}

	/* The two that are movement rather than a state change, and so have
	   nothing to settle into. */
	.att-verify--scanning::after,
	.att-result--valid .att-result__icon::before { display: none; }

	.att-drop:hover,
	.att-drop--over,
	.att-drop:hover .att-drop__icon,
	.att-drop--over .att-drop__icon,
	.att-verify__button:hover:not(:disabled),
	.att-verify__button:active:not(:disabled) { transform: none; }
}

@media print {
	.att-verify {
		border: 0;
		box-shadow: none;
		padding: 0;
		overflow: visible;
	}

	.att-verify::before,
	.att-verify::after { display: none; }

	.att-verify__form,
	.att-verify__upload,
	.att-verify__hint,
	.att-tabs,
	.att-downloads,
	.att-cert__pager,
	.att-qr figcaption { display: none; }

	/* On paper there is no paging, so every page of the document prints and
	   each starts a sheet of its own. The pager controls go with the rest of
	   the interface — printed arrows are not clickable and only take up room
	   at the foot of the last page. */
	.att-cert__page,
	.att-cert--paged .att-cert__page { display: block; }
	.att-cert__page + .att-cert__page { break-before: page; margin-top: 12px; }

	.att-result { border: 1px solid #ccc; break-inside: avoid; }

	/* Printing the page is another way to get the certificate out, so a
	   protected one is left out of the printed copy. The verification result
	   itself still prints, which is what a verifier actually needs. */
	.attestra-protected .att-cert--protected { display: none; }
}
