/*
 * the-loom — "Werkstattpanel" visual direction.
 * Dark instrument-panel language: charcoal panels, brass readouts,
 * IBM Plex for the engineered/technical register. Deliberately single-theme
 * (no light mode) — see /RESTORE.md and the design pitch for the rationale.
 */

:root {
	--ink: #14191f;       /* page ground, one step darker than panel surfaces */
	--panel: #1b232c;     /* raised surface — the exact charcoal from the product logos */
	--panel-2: #212a33;   /* card surface, one step up from panel */
	--recess: #0f1318;    /* inset/grid surfaces (odometer readouts, bank grids) */
	--border: #303b47;
	--brass: #e0a13d;     /* the exact accent from the product logos */
	--brass-bright: #f0b75a;
	--text: #e9ebe8;
	--text-soft: #9aa2a8;
	--text-faint: #838b92; /* WCAG AA (4.5:1) against both --panel and --ink — the original #767e85 landed at 3.84:1/4.28:1 */
	--radius: 8px;
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
	margin: 0;
	background: var(--ink);
	color: var(--text);
	font-family: "IBM Plex Sans", ui-sans-serif, system-ui, sans-serif;
	line-height: 1.55;
}

a { color: var(--brass); text-decoration: none; }
a:hover { color: var(--brass-bright); text-decoration: underline; }
a:focus-visible, button:focus-visible { outline: 2px solid var(--brass); outline-offset: 2px; }
/* Joomla's email-cloak plugin (<joomla-hidden-mail>) leaves the original,
   pre-decode <a href="mailto:..."> empty in the DOM once it's upgraded the
   real link inside the custom element — empty but still focusable, so
   keyboard/screen-reader users hit a link with nothing announced. */
a[href^="mailto:"]:empty { display: none; }

img { max-width: 100%; height: auto; }

h1, h2, h3, h4 {
	font-family: "IBM Plex Sans", sans-serif;
	font-weight: 600;
	color: var(--text);
	line-height: 1.25;
	text-wrap: balance;
}

.mono {
	font-family: "IBM Plex Mono", ui-monospace, monospace;
}

.eyebrow {
	font-family: "IBM Plex Mono", monospace;
	font-size: 0.7rem;
	font-weight: 500;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--brass);
}

.wrap {
	max-width: 1100px;
	margin: 0 auto;
	padding: 0 1.5rem;
}

/* ---- Header ---------------------------------------------------------- */

.tl-header {
	position: sticky;
	top: 0;
	z-index: 20;
	background: var(--panel);
	border-bottom: 1px solid var(--border);
}
.tl-header .wrap {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1.5rem;
	padding-top: 1.1rem;
	padding-bottom: 1.1rem;
	flex-wrap: wrap;
}
.tl-wordmark {
	display: flex;
	align-items: center;
	gap: 0.6rem;
	font-family: "IBM Plex Mono", monospace;
	font-size: 0.85rem;
	font-weight: 600;
	letter-spacing: 0.2em;
	text-transform: uppercase;
	color: var(--text);
}
.tl-wordmark:hover { color: var(--brass); text-decoration: none; }
.tl-mark {
	display: flex;
	flex: none;
	width: 26px;
}
.tl-mark svg { display: block; width: 100%; height: auto; }

.tl-nav ul {
	list-style: none;
	display: flex;
	flex-wrap: wrap;
	gap: 0.35rem;
	margin: 0;
	padding: 0;
}
.tl-nav a {
	display: inline-block;
	font-family: "IBM Plex Mono", monospace;
	font-size: 0.72rem;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--text-soft);
	padding: 0.45rem 0.7rem;
	border-radius: 4px;
}
.tl-nav a:hover { color: var(--brass); background: var(--panel-2); text-decoration: none; }
.tl-nav li.current a { color: var(--brass); }

.tl-nav-toggle {
	display: none;
	align-items: center;
	justify-content: center;
	width: 36px;
	height: 36px;
	flex: none;
	border: 1px solid var(--border);
	border-radius: 4px;
	background: var(--panel-2);
	color: var(--text-soft);
	cursor: pointer;
}
.tl-nav-toggle svg { width: 20px; height: 20px; }
.tl-nav-toggle:hover { color: var(--brass); border-color: var(--brass); }

/* Below this width the nav drops to its own full-width row, collapsed by
   default — the toggle button (hidden above the breakpoint) shows/hides it. */
@media (max-width: 640px) {
	.tl-header .wrap { flex-wrap: wrap; }
	.tl-nav-toggle { display: flex; margin-left: auto; }
	.tl-nav {
		display: none;
		flex-basis: 100%;
		order: 3;
	}
	.tl-nav.is-open { display: block; }
	.tl-nav ul { flex-direction: column; gap: 0.15rem; padding: 0.75rem 0 1rem; }
	.tl-nav a { display: block; padding: 0.6rem 0.7rem; }
}

/* ---- Hero / hatch (home only) ----------------------------------------- */

.tl-hero {
	position: relative;
	padding: 6.5rem 0 1rem;
}
.tl-hero-content { position: relative; z-index: 1; }

/* Sits behind the header/content (z-index: -1), not inside .tl-hero — a
   portrait mark that big would get clipped by the hero's own height if it
   were boxed in there, so it's positioned against the page instead and
   scrolls away naturally once past the intro. The wrap-width, zero-height
   shell below gives it the same right edge as the actual page content
   (rather than the full viewport), so it ends flush there instead of
   bleeding past it and causing a horizontal scrollbar. */
.tl-page-art-wrap {
	position: relative;
	height: 0;
}
.tl-page-art {
	position: absolute;
	z-index: -1;
	top: -1.5rem;
	right: 0;
	width: min(640px, 58%);
	opacity: 0.1;
	pointer-events: none;
}
.tl-page-art svg { display: block; width: 100%; height: auto; }
@media (max-width: 720px) {
	.tl-page-art { width: 85%; top: -0.5rem; opacity: 0.07; }
	.tl-hero { padding-top: 3rem; }
}

.tl-readout {
	margin-top: 4rem;
}
.tl-readout :is(p, ul, ol) { color: var(--text-soft); max-width: 62ch; }
.tl-readout p:first-child { margin-top: 0; }
.tl-readout a { color: var(--brass); text-decoration: underline; text-underline-offset: 0.15em; }

/* ---- Banks / instrument panels ---------------------------------------- */

.tl-banks { margin-top: 2.75rem; }

.bank { margin-bottom: 2.25rem; }
.bank:last-child { margin-bottom: 0; }

.bank-label {
	display: flex;
	align-items: center;
	gap: 0.65rem;
	font-family: "IBM Plex Mono", monospace;
	font-size: 0.7rem;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--text-faint);
	margin-bottom: 0.75rem;
}
.bank-label b { color: var(--brass); font-weight: 600; }
.bank-label a { color: var(--text-faint); text-decoration: none; }
.bank-label a:hover { color: var(--brass); }
.bank-label::after { content: ""; flex: 1; height: 1px; background: var(--border); }

.bank-grid {
	display: grid;
	/* auto-fill, not auto-fit: keeps every column at the same width a fully
	   packed row would use, leaving empty tracks blank instead of stretching
	   a bank's few cards to fill the row. */
	grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
	gap: 1px;
	background-color: var(--recess);
	/* Pegboard texture for empty slots — a plain repeating tile, not aligned
	   per cell (auto-fill's column count changes with viewport width, so
	   there's no fixed cell size to align to). Occupied cards paint over it
	   with their own opaque background; it only shows in gaps and empty
	   tracks, reading as the wall behind the instruments. */
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='44' height='44'%3E%3Ccircle cx='22' cy='22' r='1.6' fill='%23303b47'/%3E%3C/svg%3E");
	border: 1px solid var(--border);
	border-radius: var(--radius);
	overflow: hidden;
}

.instrument {
	position: relative;
	background: var(--panel-2);
	padding: 1.3rem 1.25rem;
	display: flex;
	flex-direction: column;
	transition: background-color 0.15s ease;
}

.instrument-border {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	pointer-events: none;
}
.instrument-border path {
	fill: none;
	stroke: var(--brass);
	stroke-width: 2px;
	/* Perimeter of the 0 0 100 100 viewBox square = 400 user units, constant
	   regardless of the card's actual rendered size. (No non-scaling-stroke:
	   it makes the browser compute dash length in post-transform screen space
	   instead of this local space, which breaks the offset math below. The
	   trade-off is a slightly uneven stroke width between edges on very
	   non-square cards — negligible at 2px.) */
	stroke-dasharray: 400;
	stroke-dashoffset: 400;
	transition: stroke-dashoffset 0.6s ease;
}
.instrument-link:hover .instrument-border path { stroke-dashoffset: 0; }
.instrument .icon {
	width: 2.3rem;
	height: 2.3rem;
	border-radius: 50%;
	border: 1px solid var(--brass);
	display: flex;
	align-items: center;
	justify-content: center;
	color: var(--brass);
	margin-bottom: 0.9rem;
}
.instrument .icon svg { width: 1.15rem; height: 1.15rem; }
.instrument .name {
	font-weight: 600;
	font-size: 0.98rem;
	color: var(--text);
}
.instrument .desc {
	font-size: 0.82rem;
	color: var(--text-soft);
	margin: 0.4rem 0 0.9rem;
	line-height: 1.5;
	flex: 1;
}
.odometer {
	display: inline-flex;
	align-self: flex-start;
	font-family: "IBM Plex Mono", monospace;
	font-size: 0.72rem;
	background: var(--recess);
	border: 1px solid var(--border);
	border-radius: 4px;
	overflow: hidden;
}
.odometer span {
	padding: 0.22rem 0.45rem;
	border-right: 1px solid var(--border);
	color: var(--brass);
}
.odometer span:last-child { border-right: none; color: var(--text-faint); }

a.instrument-link {
	color: inherit;
	text-decoration: none;
	cursor: pointer;
}
.instrument-link:hover { background: #2b3540; }
.instrument-link:hover .name { color: var(--brass); }
.instrument-link:hover .icon { background: var(--brass); color: var(--panel-2); }

.instrument:not(.instrument-link) {
	/* 0.68 faded the odometer digits below WCAG AA contrast (4.41:1 measured,
	   needs 4.5:1) — 0.72 is barely different to look at and clears it. */
	opacity: 0.72;
}
.instrument:not(.instrument-link) .desc::after {
	content: "Not yet published";
	display: block;
	margin-top: 0.5rem;
	font-family: "IBM Plex Mono", monospace;
	font-size: 0.65rem;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--text-faint);
}

/* ---- Layout: content + sidebar ---------------------------------------- */

.tl-layout {
	display: grid;
	/* minmax(0, 1fr), not a bare 1fr — a grid item's implicit min-width is
	   its content's max-content size, so without the 0 floor a nowrap
	   descendant (e.g. the release-log summary line) blows the column,
	   and the whole page, wider than the viewport. */
	grid-template-columns: minmax(0, 1fr);
	gap: 2.5rem;
	margin-top: 3rem;
	align-items: start;
}
.tl-layout.has-sidebar { grid-template-columns: minmax(0, 1fr) 280px; }

.tl-content {
	background: var(--panel);
	border: 1px solid var(--border);
	border-radius: var(--radius);
	padding: 2rem 2.25rem;
}
.tl-content :is(h1, h2, h3) { color: var(--text); }
.tl-content h2 { font-size: 1.3rem; margin-top: 2rem; }
.tl-content p, .tl-content li { color: var(--text-soft); max-width: 68ch; }
.tl-content a { color: var(--brass); }
/* Links inline in running text need a non-colour cue too (WCAG 1.4.1) —
   scoped to prose rather than the blanket .tl-content a above, so heading
   links (subcategory/product titles) and other non-text-block links don't
   pick up an underline they don't need. */
.tl-content p a, .tl-content li a { text-decoration: underline; text-underline-offset: 0.15em; }
.tl-content .page-header h1,
.tl-content h1:first-child { margin-top: 0; }
/* "Subcategories" heading on bank category pages — a plain unclassed <h3>
   direct child of the children wrapper; the per-product headings below it
   sit one level deeper, so this selector doesn't touch them. */
.tl-content .cat-children > h3 { display: none; }

/* Product custom fields (Version, Toolicon, Tagline, Released) are only
   there to feed the homepage/bank cards — Joomla has no per-field switch to
   stop them auto-rendering into the category description, so drop the list
   Joomla injects at the top of it here instead. */
.tl-content .category-desc .fields-container { display: none; }

/* Bank category pages (Joomla/Wordpress/Prestashop Extensions, Applications):
   the category's own title + intro sit bare on the background, like the
   homepage hero — framing moves onto each subcategory block instead of
   wrapping the whole page in one box. */
.tl-content--bank {
	background: none;
	border: none;
	padding: 0;
}
.tl-content--bank .cat-children {
	margin-top: 2.5rem;
	display: flex;
	flex-direction: column;
	gap: 1.25rem;
}
.tl-content--bank .com-content-category-blog__child {
	background: var(--panel);
	border: 1px solid var(--border);
	border-radius: var(--radius);
	padding: 1.75rem 2rem;
}
.tl-content--bank .com-content-category-blog__child > h2:first-child {
	margin-top: 0;
}

/* Product page release log — replaces Joomla's default article listing
   (templates/the-loom/html/com_content/category/blog.php). One row per
   release, expandable in place instead of linking out to an article page. */
.tl-release-log {
	margin-top: 2rem;
	border-top: 1px solid var(--border);
}
.tl-release {
	border-bottom: 1px solid var(--border);
}
.tl-release > summary {
	list-style: none;
	display: flex;
	align-items: baseline;
	gap: 1rem;
	padding: 0.7rem 0.25rem;
	cursor: pointer;
}
.tl-release > summary::-webkit-details-marker { display: none; }
.tl-release > summary:hover { background: var(--panel-2); }
.tl-release > summary::before {
	content: "\203A";
	flex: none;
	width: 0.7rem;
	color: var(--text-faint);
	transition: transform 0.15s ease;
}
.tl-release[open] > summary::before { transform: rotate(90deg); }
.tl-release-version {
	font-family: "IBM Plex Mono", monospace;
	font-weight: 600;
	color: var(--brass);
	flex: none;
	width: 4rem;
}
.tl-release.is-current .tl-release-version { color: var(--brass-bright); }
.tl-release-date {
	font-family: "IBM Plex Mono", monospace;
	font-size: 0.78rem;
	color: var(--text-faint);
	flex: none;
	width: 8rem;
}
.tl-release-summary {
	color: var(--text-soft);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	flex: 1;
	min-width: 0;
}
.tl-release-flag {
	flex: none;
	font-family: "IBM Plex Mono", monospace;
	font-size: 0.65rem;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--brass-bright);
	border: 1px solid #4a3a20;
	background: rgba(224, 161, 61, 0.1);
	border-radius: 3px;
	padding: 0.05rem 0.4rem;
}
.tl-release-body {
	padding: 0 0.25rem 1rem 5.7rem;
	color: var(--text-soft);
	max-width: 62ch;
}
.tl-release-body :is(p, ul, ol) { margin: 0 0 0.6rem; }
.tl-release-body :last-child { margin-bottom: 0; }
@media (max-width: 600px) {
	.tl-release-date { display: none; }
	.tl-release-body { padding-left: 1.7rem; }
}

.tl-content img { border-radius: 4px; }
.tl-content pre, .tl-content code {
	font-family: "IBM Plex Mono", monospace;
	background: var(--recess);
	border: 1px solid var(--border);
	border-radius: 4px;
}
.tl-content code { padding: 0.1rem 0.35rem; font-size: 0.85em; }
.tl-content pre { padding: 1rem; overflow-x: auto; }
.tl-content table { border-collapse: collapse; max-width: 100%; overflow-x: auto; display: block; }
.tl-content table :is(th, td) { border: 1px solid var(--border); padding: 0.5rem 0.75rem; }

.tl-sidebar {
	background: var(--panel);
	border: 1px solid var(--border);
	border-radius: var(--radius);
	padding: 1.5rem 1.5rem 1.75rem;
	align-self: start;
}
.tl-sidebar .eyebrow { display: block; margin-bottom: 0.9rem; }
.tl-sidebar :is(ul, ol) { list-style: none; margin: 0; padding: 0; }
.tl-sidebar li {
	padding: 0.55rem 0;
	border-top: 1px solid var(--border);
	font-size: 0.88rem;
}
.tl-sidebar li:first-child { border-top: none; }
.tl-sidebar a { color: var(--text-soft); }
.tl-sidebar a:hover { color: var(--brass); }

/* ---- Convert Forms ------------------------------------------------------
   convertforms.css builds every field from CSS custom properties, normally
   supplied per-form by Convert Forms' own style panel as inline <style>
   blocks rendered into the page. Clearing that panel in the admin doesn't
   stop Convert Forms from emitting those blocks — it just leaves them at
   its own factory defaults (white inputs, Google-blue primary, etc.), and
   because they're scoped to the specific form's #cf_N id, they out-rank a
   plain .convertforms class rule. !important on every custom property here
   is what actually wins that fight, regardless of what any given form's
   generated style block still says. A few things outside the variable API
   (letter-spacing, focus ring, hover colour) get a plain rule alongside. */
.convertforms {
	--font: "IBM Plex Sans", ui-sans-serif, sans-serif !important;
	--max-width: none !important;
	--background-color: transparent !important;
	--border: none !important;
	--border-radius: 0 !important;
	--padding: 0 !important;
	--control-gap: 0.55rem !important;

	--label-color: var(--text-faint) !important;
	--label-size: 0.78rem !important;
	--label-weight: 600 !important;

	--input-color: var(--text) !important;
	--input-background-color: var(--recess) !important;
	--input-border-color: #303b47 !important; /* the site's --border value — a plain colour here, since --border above is a full shorthand (used for the form wrapper's own border) rather than a colour */
	--input-border-radius: 6px !important;
	--input-padding: 0.65rem 0.85rem !important;
	--input-text-align: left !important;
	--input-placeholder-color: var(--text-faint) !important;
	--input-size: 0.92rem !important;

	--button-background-color: var(--brass) !important;
	--button-color: var(--ink) !important;
	--button-border: none !important;
	--button-border-radius: 6px !important;
	--button-padding: 0.65rem 1.4rem !important;
	--button-font-size: 0.78rem !important;

	--color-primary: var(--brass) !important;
	--color-success: #5cb87a !important;
	--color-danger: #e2685c !important;
	--color-grey: #303b47 !important;

	--heading-font-size: 1.3rem !important;
	--heading-line-height: 1.3 !important;
	--heading-letter-spacing: normal !important;
	--heading-text-align: left !important;
}

.convertforms .cf-content-text { color: var(--text-soft); }
.convertforms .cf-fields { margin-top: 1.25rem; }
.convertforms .cf-label {
	font-family: "IBM Plex Mono", monospace;
	letter-spacing: 0.06em;
	text-transform: uppercase;
}
.convertforms .cf-input:focus {
	border-color: var(--brass);
}
.convertforms .cf-input:focus-visible {
	outline: 2px solid var(--brass);
	outline-offset: 1px;
}
.convertforms textarea.cf-input { resize: vertical; }
.convertforms .cf-btn {
	font-family: "IBM Plex Mono", monospace;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	cursor: pointer;
}
.convertforms .cf-btn:hover { opacity: 1; background: var(--brass-bright); }
.convertforms .cf-select select {
	background-image: linear-gradient(45deg, transparent 50%, var(--text-faint) 50%), linear-gradient(135deg, var(--text-faint) 50%, transparent 50%) !important;
	background-position: calc(100% - 18px) center, calc(100% - 13px) center !important;
	background-size: 5px 5px, 5px 5px !important;
	background-repeat: no-repeat !important;
}

/* ---- Footer ------------------------------------------------------------ */

.tl-footer {
	margin-top: 4rem;
	border-top: 1px solid var(--border);
	background: var(--panel);
}
.tl-footer .wrap {
	padding: 2rem 1.5rem;
	display: flex;
	justify-content: space-between;
	align-items: center;
	flex-wrap: wrap;
	gap: 1rem;
}
.tl-footer .legal {
	font-family: "IBM Plex Mono", monospace;
	font-size: 0.72rem;
	color: var(--text-faint);
}
.tl-footer .legal a { color: var(--text-soft); }
.tl-footer .custom {
	font-family: "IBM Plex Mono", monospace;
	font-size: 0.72rem;
	color: var(--text-faint);
}
/* The "Built with Joomla!" custom-HTML module wraps its text in a bare <p>,
   whose default 1rem bottom margin (from bootstrap.css) made this column
   taller than its .legal/menu siblings — with the row centred, that pushed
   the visible text above their baseline instead of level with it. */
.tl-footer .custom p { margin: 0; }
.tl-footer .custom a { color: var(--text-soft); }

/* Footer legal-links menu module (Imprint, Privacy Policy) — rendered with
   style="none", so its <ul> lands as a bare child of .tl-footer .wrap. */
.tl-footer .wrap > ul {
	display: flex;
	flex-wrap: wrap;
	gap: 1.25rem;
	list-style: none;
	margin: 0;
	padding: 0;
	font-family: "IBM Plex Mono", monospace;
	font-size: 0.72rem;
	letter-spacing: 0.04em;
}
.tl-footer .wrap > ul a {
	color: var(--text-soft);
	text-decoration: none;
}
.tl-footer .wrap > ul a:hover { color: var(--brass); }

/* ---- System messages ---------------------------------------------------- */

#system-message-container {
	max-width: 1100px;
	margin: 1.5rem auto 0;
	padding: 0 1.5rem;
}
.alert {
	border: 1px solid var(--border);
	border-left: 3px solid var(--brass);
	background: var(--panel);
	color: var(--text-soft);
	border-radius: 4px;
	padding: 0.9rem 1.1rem;
	margin-bottom: 0.75rem;
}

@media (max-width: 760px) {
	.tl-layout.has-sidebar { grid-template-columns: 1fr; }
	.tl-content { padding: 1.5rem; }
}
