/* ==========================================================================
   Widgets — generic interactive elements embedded into contents.
   Styled with the frontend theme tokens (public/css/theme.css).
   ========================================================================== */

.widget {
	--bb-seg-a: color-mix(in srgb, var(--theme-color) 8%, var(--theme-bg));
	--bb-seg-b: color-mix(in srgb, var(--theme-color) 18%, var(--theme-bg));
}

/* --- Blackbox cipher dial ------------------------------------------------- */

.widget-blackbox .bb-stage {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 1.25rem;
}

.widget-blackbox .bb-disc-wrap {
	position: relative;
	width: 25rem;
    max-width: 100%;
	aspect-ratio: 1 / 1;
	user-select: none;
	touch-action: manipulation;
}

/* static background image / dial */
.widget-blackbox .bb-bg {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	cursor: pointer;
	-webkit-user-drag: none;
}

.widget-blackbox .bb-bg-fallback {
	border: 1px solid var(--theme-border-color);
}

.widget-blackbox .bb-seg-label {
	position: absolute;
	top: 50%;
	left: 50%;
	font-size: 0.8rem;
	font-weight: 700;
	color: var(--theme-color);
	pointer-events: none;
}

/* rotatable square with the arrow — sits centred on the background and turns
   around its own centre. pointer-events off so clicks reach the background. */
.widget-blackbox .bb-rotor {
	position: absolute;
	top: 50%;
	left: 50%;
	aspect-ratio: 1 / 1;
	transform-origin: 50% 50%;
	transform: translate(-50%, -50%);
	transition: transform 550ms cubic-bezier(0.22, 1, 0.36, 1);
	pointer-events: none;
	z-index: 2;
}

.widget-blackbox .bb-rotor svg {
	display: block;
	width: 100%;
	height: 100%;
	filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.4));
}

.widget-blackbox .bb-form {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 0.5rem;
	width: 100%;
	max-width: 25rem;
}

.widget-blackbox .bb-input {
	flex: 1 1 12rem;
	min-width: 0;
	padding: 0.5rem 0.9rem;
	color: var(--theme-color);
	background: var(--theme-bg);
	border: 1px solid var(--theme-border-color);
	border-radius: var(--theme-radius);
	text-align: center;
	letter-spacing: 0.08em;
}

.widget-blackbox .bb-submit {
	flex: 0 0 auto;
	padding: 0.5rem 1.25rem;
	border: 0;
	border-radius: var(--theme-radius);
	font-weight: 700;
	cursor: pointer;
}

.widget-blackbox .bb-submit:disabled {
	opacity: 0.6;
	cursor: default;
}

/* while a submission is loading, the square pulses in the configured colour
   (--bb-pulse, set per widget) without changing size */
.widget-blackbox .bb-rotor.is-loading svg rect {
	animation: bb-pulse 1s ease-in-out infinite;
}

@keyframes bb-pulse {
	0%, 100% { fill: #1f2733; }
	50%      { fill: var(--bb-pulse, #7fbe87); }
}

.widget-blackbox .bb-output {
	min-height: 1.5em;
	font-family: "Space Mono", monospace;
	font-size: 1.25rem;
	letter-spacing: 0.14em;
	text-align: center;
	color: var(--theme-color);
	word-break: break-word;
}

.widget-blackbox .bb-output.bb-reveal {
	animation: bb-reveal 500ms ease-out;
}

@keyframes bb-reveal {
	from { opacity: 0; transform: scale(0.92); }
	to   { opacity: 1; transform: scale(1); }
}

/* --- Kartei card index ---------------------------------------------------- */

.widget-kartei .k-stage {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 1rem;
}

.widget-kartei .k-form {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 0.5rem;
	width: 100%;
	max-width: 25rem;
}

.widget-kartei .k-input {
	flex: 1 1 12rem;
	min-width: 0;
	padding: 0.5rem 0.9rem;
	color: var(--theme-color);
	background: var(--theme-bg);
	border: 1px solid var(--theme-border-color);
	border-radius: var(--theme-radius);
}

.widget-kartei .k-submit {
	flex: 0 0 auto;
	padding: 0.5rem 1.25rem;
	border: 0;
	border-radius: var(--theme-radius);
	font-weight: 700;
	cursor: pointer;
}

.widget-kartei .k-submit:disabled {
	opacity: 0.6;
	cursor: default;
}

.widget-kartei .k-status {
	min-height: 1.5em;
	text-align: center;
	color: var(--theme-color);
}

/* --- Puzzle --------------------------------------------------------------- */

/* The stage uses board-pixel coordinates internally and is CSS-scaled to fit
   the container (transform + width/height set inline by public/js/widgets/puzzle.js). */
.widget-puzzle .pz-wrap {
	position: relative;
	width: 100%;
	overflow: hidden;
	user-select: none;
	touch-action: none;
}

.widget-puzzle .pz-stage {
	position: absolute;
	top: 0;
	left: 0;
	transform-origin: 0 0;
}

/* board placement guide — a striped backdrop the assembled image covers as it
   is solved. It lives in the unscaled wrap, so the stripes keep a fixed pixel
   size and stay visible however far a large image is scaled down. */
.widget-puzzle .pz-frame,
.widget-puzzle .pz-tray {
	position: absolute;
	border: 1px solid var(--theme-border-color);
	border-radius: var(--theme-radius);
	background-color: color-mix(in srgb, var(--theme-color) 5%, var(--theme-bg));
	pointer-events: none;
}

.widget-puzzle .pz-frame {
	background-image: repeating-linear-gradient(
		45deg,
		transparent 0,
		transparent 9px,
		color-mix(in srgb, var(--theme-color) 12%, transparent) 9px,
		color-mix(in srgb, var(--theme-color) 12%, transparent) 18px
	);
}

.widget-puzzle .pz-piece {
	position: absolute;
	transform-origin: 50% 50%;
	overflow: visible;
	/* only the clipped image hit-tests; the piece's transparent bounding box must
	   not steal the pointer from an overlapping piece underneath it */
	pointer-events: none;
	filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.35));
	transition: transform 180ms cubic-bezier(0.22, 1, 0.36, 1);
}

.widget-puzzle .pz-piece.is-dragging {
	transition: none;
	filter: drop-shadow(0 6px 12px rgba(0, 0, 0, 0.45));
}

/* only the clipped image area is interactive, so overlapping piece boxes never
   steal the pointer from the piece actually under the cursor */
.widget-puzzle .pz-img {
	pointer-events: auto;
	cursor: grab;
	-webkit-user-drag: none;
}

.widget-puzzle .pz-piece.is-dragging .pz-img {
	cursor: grabbing;
}

.widget-puzzle .pz-piece.is-locked .pz-img {
	pointer-events: none;
	cursor: default;
}

/* piece border: two stacked strokes, width driven by --pz-edge-w (set per frame
   in fitScale so it stays a fixed screen size despite the stage's CSS scale) */
.widget-puzzle .pz-edge {
	fill: none;
	pointer-events: none;
	stroke-linejoin: round;
}

.widget-puzzle .pz-edge-under {
	stroke: rgba(0, 0, 0, 0.5);
	stroke-width: var(--pz-edge-w-under, 2.6);
}

.widget-puzzle .pz-edge-over {
	stroke: rgba(255, 255, 255, 0.92);
	stroke-width: var(--pz-edge-w-over, 1.2);
}

.widget-puzzle .pz-hint,
.widget-puzzle .pz-empty {
	margin-top: 0.75rem;
	text-align: center;
	color: var(--theme-color);
	opacity: 0.8;
	font-size: 0.9rem;
}

/* solved message — overlays the (now empty) tray at real pixels, so it takes no
   extra vertical space and the text is not shrunk by the stage's CSS scale */
.widget-puzzle .pz-solution {
	position: absolute;
	display: flex;
	align-items: center;
	justify-content: center;
	box-sizing: border-box;
	padding: 0.5rem 1rem;
	text-align: center;
	font-weight: 700;
	font-size: 1.15rem;
	color: var(--theme-color);
	pointer-events: none;
	opacity: 0;
}

.widget-puzzle .pz-solution.pz-reveal {
	animation: pz-reveal 500ms ease-out forwards;
}

@keyframes pz-reveal {
	from { opacity: 0; transform: scale(0.92); }
	to   { opacity: 1; transform: scale(1); }
}

/* --- Sortieren ------------------------------------------------------------ */

.widget-sort .sort-list {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	max-width: 40rem;
	margin: 0 auto;
}

.widget-sort .sort-item {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	padding: 0.75rem 1rem;
	color: var(--theme-color);
	background: var(--theme-bg);
	border: 1px solid var(--theme-border-color);
	border-radius: var(--theme-radius);
	user-select: none;
}

/* only the grip is draggable, so touching the item body still scrolls the page */
.widget-sort .sort-grip {
	flex: 0 0 auto;
	color: var(--theme-color);
	opacity: 0.4;
	letter-spacing: -0.15em;
	font-weight: 700;
	cursor: grab;
	touch-action: none;
}

.widget-sort .sort-grip:active {
	cursor: grabbing;
}

.widget-sort .sort-static .sort-grip {
	cursor: default;
}

.widget-sort .sort-item-body {
	flex: 1 1 auto;
	min-width: 0;
}

/* SortableJS state classes */
.widget-sort .sort-ghost {
	opacity: 0.4;
}

.widget-sort .sort-chosen {
	box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
}

/* solved state: lock the look, drop the grab cursor */
.widget-sort .sort-list.is-solved .sort-item {
	cursor: default;
	border-color: color-mix(in srgb, var(--theme-color) 45%, var(--theme-border-color));
}

.widget-sort .sort-output {
	min-height: 1.5em;
	margin-top: 1rem;
	text-align: center;
	font-weight: 700;
	color: var(--theme-color);
	opacity: 0;
}

.widget-sort .sort-output.sort-reveal {
	animation: pz-reveal 500ms ease-out forwards;
}

/* --- Zuordnen ------------------------------------------------------------- */

.widget-zuordnen .zo-root {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 1rem;
}

.widget-zuordnen .zo-edge {
	fill: none;
	stroke: rgba(0, 0, 0, 0.4);
	stroke-width: 1;
	vector-effect: non-scaling-stroke;
	pointer-events: none;
}

/* draggable card (text) and tile (image) */
.widget-zuordnen .zo-chip,
.widget-zuordnen .zo-tile {
	padding: 0.35rem 0.6rem;
	color: var(--theme-color);
	background: var(--theme-bg);
	border: 1px solid var(--theme-border-color);
	border-radius: var(--theme-radius);
	cursor: grab;
	user-select: none;
	touch-action: none;
}

.widget-zuordnen .zo-tile {
	padding: 0.25rem;
	line-height: 0;
}

.widget-zuordnen .zo-chip:active,
.widget-zuordnen .zo-tile:active {
	cursor: grabbing;
}

.widget-zuordnen .zo-tray {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	align-items: center;
	gap: 0.5rem;
	width: 100%;
	min-height: 3rem;
	padding: 0.5rem;
	border: 1px dashed var(--theme-border-color);
	border-radius: var(--theme-radius);
}

/* images-as-targets mode: the reconstructed board with drop zones over pieces */
.widget-zuordnen .zo-board {
	position: relative;
	margin: 0 auto;
	max-width: 100%;
}

.widget-zuordnen .zo-piece {
	pointer-events: none;
}

.widget-zuordnen .zo-target {
	position: absolute;
	z-index: 2;
	display: flex;
	flex-wrap: wrap;
	align-content: flex-start;
	gap: 2px;
	padding: 2px;
	border: 1px dashed color-mix(in srgb, var(--theme-color) 40%, transparent);
	border-radius: 4px;
	overflow: visible;
}

.widget-zuordnen .zo-target .zo-chip,
.widget-zuordnen .zo-target .zo-tile {
	background: color-mix(in srgb, var(--theme-bg) 85%, transparent);
	font-size: 0.85rem;
}

/* texts-as-targets mode: a column of described boxes */
.widget-zuordnen .zo-textboxes {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	width: 100%;
	max-width: 42rem;
}

.widget-zuordnen .zo-textbox {
	border: 1px solid var(--theme-border-color);
	border-radius: var(--theme-radius);
	padding: 0.6rem 0.8rem;
	color: var(--theme-color);
}

.widget-zuordnen .zo-textbox-label {
	margin-bottom: 0.4rem;
}

.widget-zuordnen .zo-drop {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	min-height: 4.5rem;
	padding: 0.4rem;
	border: 1px dashed var(--theme-border-color);
	border-radius: 4px;
}

.widget-zuordnen .zo-empty {
	text-align: center;
	color: var(--theme-color);
	opacity: 0.8;
}

.widget-zuordnen .zo-output {
	min-height: 1.5em;
	text-align: center;
	font-weight: 700;
	font-size: 1.15rem;
	color: var(--theme-color);
	opacity: 0;
}

.widget-zuordnen .zo-output.zo-reveal {
	animation: pz-reveal 500ms ease-out forwards;
}

/* --- NumPad --------------------------------------------------------------- */

.widget-numpad .np-stage {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 1rem;
}

.widget-numpad .np-display {
	min-height: 2rem;
	font-size: 1.8rem;
	line-height: 1;
	letter-spacing: 0.35em;
	color: var(--theme-color);
}

.widget-numpad .np-pad {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 0.5rem;
	width: 100%;
	max-width: 15rem;
}

.widget-numpad .np-key {
	aspect-ratio: 1 / 1;
	font-size: 1.4rem;
	font-weight: 700;
	color: var(--theme-color);
	background: var(--theme-bg);
	border: 1px solid var(--theme-border-color);
	border-radius: var(--theme-radius);
	cursor: pointer;
	user-select: none;
}

.widget-numpad .np-key:active {
	background: color-mix(in srgb, var(--theme-color) 12%, var(--theme-bg));
}

.widget-numpad .np-key:disabled {
	opacity: 0.6;
	cursor: default;
}

.widget-numpad .np-key.np-action {
	font-size: 1.1rem;
}

.widget-numpad .np-submit {
	width: 100%;
	max-width: 15rem;
	padding: 0.6rem 1rem;
	border: 0;
	border-radius: var(--theme-radius);
	font-weight: 700;
	cursor: pointer;
}

.widget-numpad .np-submit:disabled {
	opacity: 0.6;
	cursor: default;
}

.widget-numpad .np-output {
	min-height: 1.5em;
	text-align: center;
	font-weight: 700;
	font-size: 1.15rem;
	color: var(--theme-color);
	opacity: 0;
}

.widget-numpad .np-output.np-reveal {
	animation: pz-reveal 500ms ease-out forwards;
}

.widget-numpad .np-stage.is-wrong {
	animation: np-shake 400ms ease;
}

@keyframes np-shake {
	0%, 100% { transform: translateX(0); }
	20% { transform: translateX(-8px); }
	40% { transform: translateX(8px); }
	60% { transform: translateX(-6px); }
	80% { transform: translateX(6px); }
}

/* --- Auswahl -------------------------------------------------------------- */

.widget-auswahl .aw-stage {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 1rem;
}

.widget-auswahl .aw-list {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	width: 100%;
	max-width: 40rem;
}

.widget-auswahl .aw-item {
	padding: 0.75rem 1rem;
	color: var(--theme-color);
	background: var(--theme-bg);
	border: 1px solid var(--theme-border-color);
	border-radius: var(--theme-radius);
	cursor: pointer;
	user-select: none;
	transition: background 120ms ease, border-color 120ms ease;
}

.widget-auswahl .aw-item.is-selected {
	font-weight: 600;
	border-color: color-mix(in srgb, var(--theme-color) 55%, var(--theme-border-color));
	background: color-mix(in srgb, var(--theme-color) 10%, var(--theme-bg));
}

.widget-auswahl .aw-item.is-locked {
	cursor: default;
}

.widget-auswahl .aw-submit {
	width: 100%;
	max-width: 40rem;
	padding: 0.6rem 1rem;
	border: 0;
	border-radius: var(--theme-radius);
	font-weight: 700;
	cursor: pointer;
}

.widget-auswahl .aw-submit:disabled {
	opacity: 0.6;
	cursor: default;
}

.widget-auswahl .aw-output {
	min-height: 1.5em;
	text-align: center;
	font-weight: 700;
	font-size: 1.15rem;
	color: var(--theme-color);
	opacity: 0;
}

.widget-auswahl .aw-output.aw-reveal {
	animation: pz-reveal 500ms ease-out forwards;
}

.widget-auswahl .aw-stage.is-wrong {
	animation: np-shake 400ms ease;
}

/* --- Text markieren ------------------------------------------------------- */

.widget-markieren .mk-stage {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 1rem;
}

.widget-markieren .mk-text {
	width: 100%;
	max-width: 40rem;
	color: var(--theme-color);
	line-height: 2;
	text-align: left;
}

.widget-markieren .mk-item {
	padding: 0.1em 0.15em;
	border-radius: var(--theme-radius);
	cursor: pointer;
	user-select: none;
	background: transparent;
	box-shadow: inset 0 0 0 1px transparent;
	transition: background 120ms ease, box-shadow 120ms ease;
}

.widget-markieren .mk-item:hover,
.widget-markieren .mk-item:focus-visible {
	background: color-mix(in srgb, var(--theme-color) 8%, transparent);
	box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--theme-color) 35%, transparent);
	outline: none;
}

.widget-markieren .mk-item.is-marked,
.widget-markieren .mk-item.is-marked:hover {
	font-weight: 600;
	background: color-mix(in srgb, var(--theme-color) 30%, transparent);
	box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--theme-color) 55%, transparent);
}

.widget-markieren .mk-item.is-locked {
	cursor: default;
}

.widget-markieren .mk-submit {
	width: 100%;
	max-width: 40rem;
	padding: 0.6rem 1rem;
	border: 0;
	border-radius: var(--theme-radius);
	font-weight: 700;
	cursor: pointer;
}

.widget-markieren .mk-submit:disabled {
	opacity: 0.6;
	cursor: default;
}

.widget-markieren .mk-output {
	min-height: 1.5em;
	text-align: center;
	font-weight: 700;
	font-size: 1.15rem;
	color: var(--theme-color);
	opacity: 0;
}

.widget-markieren .mk-output.mk-reveal {
	animation: pz-reveal 500ms ease-out forwards;
}

.widget-markieren .mk-stage.is-wrong {
	animation: np-shake 400ms ease;
}

/* --- Taschenlampe (reveal image with a flashlight) ----------------------- */

.widget-taschenlampe .tl-stage {
	position: relative;
	display: inline-block;
	max-width: 100%;
	line-height: 0;
}

.widget-taschenlampe .tl-img {
	display: block;
	width: 100%;
	height: auto;
	user-select: none;
	-webkit-user-drag: none;
}

.widget-taschenlampe .tl-canvas {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	touch-action: none;
	cursor: crosshair;
}

/* --- Cäsar-Chiffre (two-disc cipher wheel) ------------------------------- */

.widget-caesar .cs-stage {
	display: flex;
	flex-direction: column;
	align-items: center;
}

.widget-caesar .cs-wrap {
	position: relative;
	width: 26rem;
	max-width: 100%;
	aspect-ratio: 1 / 1;
	user-select: none;
}

.widget-caesar .cs-svg {
	display: block;
	width: 100%;
	height: 100%;
	overflow: visible;
	touch-action: none;
}

.widget-caesar .cs-disc {
	stroke: var(--theme-border-color);
	stroke-width: 1.5;
}

.widget-caesar .cs-disc-outer {
	fill: color-mix(in srgb, var(--theme-color) 8%, var(--theme-bg));
}

.widget-caesar .cs-disc-inner {
	fill: color-mix(in srgb, var(--theme-color) 18%, var(--theme-bg));
}

.widget-caesar .cs-divider {
	stroke: color-mix(in srgb, var(--theme-color) 35%, var(--theme-border-color));
	stroke-width: 1;
}

.widget-caesar .cs-divider-outer {
	stroke-width: 1.25;
}

.widget-caesar .cs-label {
	fill: var(--theme-color);
	font-weight: 700;
	font-family: inherit;
}

.widget-caesar .cs-marker {
	fill: var(--theme-color);
}

.widget-caesar .cs-hub {
	fill: color-mix(in srgb, var(--theme-color) 55%, var(--theme-bg));
	stroke: var(--theme-border-color);
	stroke-width: 1.5;
}

/* the inner disc rotates around the viewBox centre */
.widget-caesar .cs-inner {
	transform-box: view-box;
	transform-origin: 200px 200px;
	cursor: grab;
}

.widget-caesar .cs-inner.is-grabbing {
	cursor: grabbing;
}

.widget-caesar .cs-inner.is-snapping {
	transition: transform 260ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

/* --- Kryptex (row of rotatable character rings) --------------------------- */

/* Cell height and ring width live here alone: the JS shifts each strip by a
   share of its own height, so a resize can never knock a wheel out of line. */
.widget-kryptex .kx-stage {
	--kx-cell: clamp(2.1rem, 9vw, 2.9rem);
	--kx-ring: clamp(1.9rem, 8vw, 2.6rem);
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 1rem;
}

.widget-kryptex .kx-case {
	position: relative;
	max-width: 100%;
	padding: 0.5rem 0.75rem;
	background: color-mix(in srgb, var(--theme-color) 8%, var(--theme-bg));
	border: 1px solid var(--theme-border-color);
	border-radius: var(--theme-radius);
	overflow-x: auto;  /* the rings spell the word, so they must not wrap */
	overflow-y: hidden;
}

.widget-kryptex .kx-track {
	position: relative;
	display: flex;
	flex-wrap: nowrap;
	gap: 0.25rem;
}

/* Marks the engaged row across all rings. Sits in the track, so it spans the
   whole ring row and scrolls with it; z-index lifts it over the ring windows,
   whose background would otherwise hide it. Only tinted, never opaque — the
   engaged characters have to stay readable through it. */
.widget-kryptex .kx-band {
	position: absolute;
	z-index: 2;
	left: 0;
	right: 0;
	top: 50%;
	height: var(--kx-cell);
	transform: translateY(-50%);
	background: color-mix(in srgb, var(--theme-color) 10%, transparent);
	border-top: 1px solid color-mix(in srgb, var(--theme-color) 35%, var(--theme-border-color));
	border-bottom: 1px solid color-mix(in srgb, var(--theme-color) 35%, var(--theme-border-color));
	pointer-events: none;
}

.widget-kryptex .kx-ring {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.15rem;
	flex: 0 0 auto;
}

.widget-kryptex .kx-arrow {
	width: var(--kx-ring);
	height: 1.25rem;
	padding: 0;
	color: var(--theme-color);
	background: transparent;
	border: 0;
	opacity: 0.55;
	cursor: pointer;
	touch-action: manipulation;
}

.widget-kryptex .kx-arrow::before {
	content: '';
	display: block;
	margin: 0 auto;
	border-left: 0.4rem solid transparent;
	border-right: 0.4rem solid transparent;
}

.widget-kryptex .kx-arrow-up::before {
	border-bottom: 0.45rem solid currentColor;
}

.widget-kryptex .kx-arrow-down::before {
	border-top: 0.45rem solid currentColor;
}

.widget-kryptex .kx-arrow:hover {
	opacity: 1;
}

.widget-kryptex .kx-arrow:disabled {
	opacity: 0.25;
	cursor: default;
}

/* ~2.4 cells tall, so the neighbours above and below stay cropped */
.widget-kryptex .kx-window {
	position: relative;
	width: var(--kx-ring);
	height: calc(var(--kx-cell) * 2.4);
	overflow: hidden;
	background: color-mix(in srgb, var(--theme-color) 16%, var(--theme-bg));
	border-radius: calc(var(--theme-radius) / 3);
	cursor: pointer;
	user-select: none;
	touch-action: manipulation; /* tap to turn — a vertical swipe still scrolls */
	/* Fades the characters towards the top and bottom edge. No border here: the
	   mask would cut it off above and below and leave it looking broken — the
	   ring is set apart by its background instead. */
	-webkit-mask-image: linear-gradient(to bottom, transparent, #000 22%, #000 78%, transparent);
	mask-image: linear-gradient(to bottom, transparent, #000 22%, #000 78%, transparent);
}

.widget-kryptex .kx-window:focus-visible {
	outline: 2px solid var(--theme-color);
	outline-offset: 2px;
}

/* the strip is taller than the window; the JS offsets it in % of its own height */
.widget-kryptex .kx-strip {
	position: absolute;
	top: 50%;
	left: 0;
	right: 0;
	will-change: transform;
}

.widget-kryptex .kx-cell {
	height: var(--kx-cell);
	display: flex;
	align-items: center;
	justify-content: center;
	font-weight: 700;
	font-size: calc(var(--kx-cell) * 0.5);
	line-height: 1;
	color: var(--theme-color);
}

.widget-kryptex .kx-case.is-wrong {
	animation: np-shake 400ms ease;
}

.widget-kryptex .kx-submit {
	width: 100%;
	max-width: 15rem;
	padding: 0.6rem 1rem;
	border: 0;
	border-radius: var(--theme-radius);
	font-weight: 700;
	cursor: pointer;
}

.widget-kryptex .kx-submit:disabled {
	opacity: 0.6;
	cursor: default;
}

.widget-kryptex .kx-output {
	min-height: 1.5em;
	text-align: center;
	font-weight: 700;
	font-size: 1.15rem;
	color: var(--theme-color);
	opacity: 0;
}

.widget-kryptex .kx-output.kx-reveal {
	animation: pz-reveal 500ms ease-out forwards;
}
