/* Product History - site design system */
:root {
    --pht-bg: #0b0e14;
    --pht-bg-alt: #11151f;
    --pht-card: #161b26;
    --pht-card-hover: #1d2333;
    --pht-border: #262d3d;
    --pht-text: #e8ebf2;
    --pht-text-muted: #9aa3b8;
    --pht-accent: #35e0a1;
    --pht-accent-2: #ff4d6d;
    --pht-warn: #f5b942;
    --pht-radius: 14px;
    font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

.pht-wrap * { box-sizing: border-box; }

/* No max-width here on purpose - this component should fill whatever column
   Joomla's own template gives it, not impose a smaller cap of its own. Only
   the vertical timeline's single-column reading list gets its own deliberate
   width cap further down, for readability rather than "available space". */
.pht-wrap {
    width: 100%;
    margin: 0 auto;
    padding: 1.75rem 1.25rem 4rem;
    color: var(--pht-text);
    background: var(--pht-bg);
}

.pht-wrap a { color: var(--pht-accent); text-decoration: none; }
.pht-wrap a:hover { text-decoration: underline; }

@media (prefers-reduced-motion: no-preference) {
    html:has(.pht-wrap) { scroll-behavior: smooth; }
}

/* ---- breadcrumb ---- */
.pht-breadcrumb {
    font-size: .85rem;
    color: var(--pht-text-muted);
    margin-bottom: 1.5rem;
}

.pht-breadcrumb a { color: var(--pht-text-muted); }
.pht-breadcrumb .sep { margin: 0 .4rem; opacity: .5; }

/* ---- hero / heading ---- */
.pht-hero {
    text-align: center;
    padding: 2.5rem 0 2rem;
}

.pht-hero h1 {
    font-size: clamp(1.8rem, 4vw, 2.8rem);
    margin: 0 0 .5rem;
    letter-spacing: -.01em;
}

.pht-hero p {
    color: var(--pht-text-muted);
    max-width: 640px;
    margin: 0 auto;
    font-size: 1.05rem;
}

/* ---- year timeline scrubber ---- */
.pht-timeline-scrubber {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    justify-content: center;
    margin: 1.75rem 0 2.5rem;
}

.pht-timeline-scrubber button {
    background: var(--pht-card);
    border: 1px solid var(--pht-border);
    color: var(--pht-text-muted);
    padding: .4rem .7rem;
    border-radius: 8px;
    font-size: .82rem;
    cursor: pointer;
    transition: all .15s ease;
}

.pht-timeline-scrubber button:hover,
.pht-timeline-scrubber button.is-active {
    border-color: var(--pht-accent);
    color: var(--pht-text);
    background: var(--pht-card-hover);
}

/* ---- filter/search bar ---- */
.pht-toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: .6rem;
    margin-bottom: 1.75rem;
    align-items: center;
}

.pht-toolbar input[type="search"],
.pht-toolbar select {
    background: var(--pht-card);
    border: 1px solid var(--pht-border);
    color: var(--pht-text);
    padding: .55rem .8rem;
    border-radius: 10px;
    font-size: .9rem;
}

.pht-toolbar input[type="search"] { flex: 1 1 220px; }

/* ---- brand grid (grid/columns come from Bootstrap's row/col in the markup;
   these rules are only the card's own look) ---- */
.pht-brand-strip { margin-bottom: 2.5rem; }

.pht-brand-tile {
    /* Query container so the brand name below can scale relative to this
       tile's own rendered width (cqw) rather than the viewport - the same
       tile sits under row-cols-3/4/6/8 at different breakpoints, so its
       actual width doesn't track viewport width in a simple way. */
    container-type: inline-size;
    height: 100%;
    background: var(--pht-card);
    border: 1px solid var(--pht-border);
    border-radius: var(--pht-radius);
    padding: 1.5rem 1rem;
    text-align: center;
    transition: transform .15s ease, border-color .15s ease, background .15s ease;
    display: block;
}

.pht-brand-tile:hover {
    transform: translateY(-3px);
    border-color: var(--pht-accent);
    background: var(--pht-card-hover);
    text-decoration: none;
}

.pht-brand-tile .logo-chip { background: #fff; border-radius: 10px; padding: .6rem .8rem; display: flex; align-items: center; justify-content: center; height: 64px; margin-bottom: .75rem; }
.pht-brand-tile img { max-width: 100%; max-height: 44px; object-fit: contain; }

/* A one-word brand name (e.g. "Showcontroller") has no space to wrap at,
   so at a narrow tile width (more columns on mobile) default CSS just lets
   it overflow past its own box instead of wrapping - confirmed directly
   (115px of unbroken text in a ~62px-wide mobile tile). font-size in cqw
   scales the name down with the tile itself, fitting every current brand
   name on one line at its natural size in the common case; overflow-wrap
   is a last-resort safety net for anything that still doesn't fit (a
   future longer name at the narrowest breakpoint) so it wraps - even
   mid-word if it must - rather than ever spilling past the tile's border.
   1rem/0.7rem clamp the fluid size so it doesn't grow oversized on a very
   wide tile or shrink past legibility on a very narrow one. */
.pht-brand-tile .name {
    font-weight: 700;
    color: var(--pht-text);
    display: block;
    font-size: clamp(.7rem, 12cqw, 1rem);
    overflow-wrap: anywhere;
}
.pht-brand-tile .meta { color: var(--pht-text-muted); font-size: .8rem; margin-top: .25rem; display: block; }

/* ---- series section headers ---- */
.pht-section-title {
    font-size: 1.1rem;
    text-transform: uppercase;
    letter-spacing: .08em;
    color: var(--pht-text-muted);
    margin: 2.25rem 0 1rem;
    display: flex;
    align-items: center;
    gap: .6rem;
}

.pht-section-title::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--pht-border);
}

.pht-section-title-compact { margin: 0 0 1rem; font-size: .95rem; }

.pht-badge {
    display: inline-block;
    padding: .18rem .55rem;
    border-radius: 999px;
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .03em;
}

.pht-badge.current { background: rgba(53,224,161,.15); color: var(--pht-accent); }
.pht-badge.discontinued { background: rgba(255,77,109,.15); color: var(--pht-accent-2); }
.pht-badge.software { background: rgba(90,140,255,.15); color: #6fa1ff; }

/* ---- series cards (grid/columns come from Bootstrap's row/col on the brand page) ---- */
.pht-series-card {
    height: 100%;
    background: var(--pht-card);
    border: 1px solid var(--pht-border);
    border-radius: var(--pht-radius);
    padding: 1.25rem;
    display: block;
    transition: transform .15s ease, border-color .15s ease, box-shadow .15s ease;
}

.pht-series-card:hover { transform: translateY(-2px); border-color: var(--pht-accent); box-shadow: 0 0 0 3px rgba(53,224,161,.08); text-decoration: none; }
.pht-series-card .title { font-weight: 700; color: var(--pht-text); font-size: 1.05rem; }
.pht-series-card .years { color: var(--pht-text-muted); font-size: .85rem; margin: .35rem 0; }

/* ---- series intro/outro ---- */
.pht-series-header {
    display: flex;
    align-items: center;
    gap: 1.25rem;
    margin-bottom: 1rem;
    flex-wrap: wrap;
}

.pht-series-header .logo-chip { background: #fff; border-radius: 10px; padding: .6rem 1rem; display: flex; align-items: center; height: 60px; }
.pht-series-header img { max-height: 40px; max-width: 180px; object-fit: contain; }

.pht-prose {
    background: var(--pht-card);
    border: 1px solid var(--pht-border);
    border-left: 3px solid var(--pht-accent);
    border-radius: 10px;
    padding: 1.1rem 1.3rem;
    margin: 1.25rem 0;
    color: var(--pht-text);
    line-height: 1.6;
}

.pht-prose.outro { border-left-color: var(--pht-accent-2); }
.pht-prose p:last-child { margin-bottom: 0; }

/* ---- product cards (grid/columns come from Bootstrap's row/col on the series page) ---- */
.pht-product-card {
    height: 100%;
    background: var(--pht-card);
    border: 1px solid var(--pht-border);
    border-radius: var(--pht-radius);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    transition: transform .15s ease, border-color .15s ease, box-shadow .15s ease;
}

.pht-product-card:hover { transform: translateY(-3px); border-color: var(--pht-accent); box-shadow: 0 0 0 3px rgba(53,224,161,.08); text-decoration: none; }

.pht-product-card .thumb {
    aspect-ratio: 4 / 3;
    /* White, uniform regardless of which source photos are actually
       transparent - see .pht-histotree-preview for why (a mix-blend-mode
       trick tried here first made every photo far too dark). */
    background: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: .75rem;
}

.pht-product-card .thumb img { max-width: 100%; max-height: 100%; object-fit: contain; }
.pht-product-card .body { padding: .9rem 1rem 1.1rem; color: var(--pht-text); flex: 1; display: flex; flex-direction: column; gap: .35rem; }
.pht-product-card .title { font-weight: 700; font-size: .95rem; }
.pht-product-card .years { color: var(--pht-text-muted); font-size: .8rem; }

/* ---- product detail (column split is Bootstrap's row/col in the markup) ---- */
.pht-product-detail { margin-top: 1rem; }

.pht-product-media { background: var(--pht-card); border: 1px solid var(--pht-border); border-radius: var(--pht-radius); padding: 1rem; position: sticky; top: 1rem; }

@media (max-width: 767.98px) {
    .pht-product-media { position: static; }
}
.pht-product-media img { width: 100%; border-radius: 8px; margin-bottom: .6rem; background: #0d1119; }
.pht-product-media a.pht-zoom { display: block; cursor: zoom-in; }
.pht-product-media a.pht-zoom:hover img { opacity: .88; }

.pht-spec-list { list-style: none; padding: 0; margin: 1rem 0; display: grid; gap: .5rem; }
.pht-spec-list li { display: flex; justify-content: space-between; border-bottom: 1px dashed var(--pht-border); padding-bottom: .4rem; font-size: .9rem; }
.pht-spec-list .k { color: var(--pht-text-muted); }

.pht-cta {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    background: var(--pht-accent);
    color: #06231a !important;
    font-weight: 700;
    padding: .7rem 1.2rem;
    border-radius: 10px;
    text-decoration: none !important;
    margin: .5rem 0;
}

.pht-cta:hover { filter: brightness(1.08); }

.pht-lineage { display: flex; gap: 1rem; margin: 1.25rem 0; flex-wrap: wrap; }
.pht-lineage a { background: var(--pht-card); border: 1px solid var(--pht-border); border-radius: 10px; padding: .6rem .9rem; color: var(--pht-text) !important; text-decoration: none !important; font-size: .85rem; }
.pht-lineage a small { display: block; color: var(--pht-text-muted); }

.pht-resource-list { list-style: none; padding: 0; margin: .5rem 0 0; display: grid; gap: .5rem; }
.pht-resource-list li { display: flex; justify-content: space-between; align-items: center; background: var(--pht-card); border: 1px solid var(--pht-border); border-radius: 10px; padding: .6rem .9rem; font-size: .88rem; }
.pht-resource-list .tag { color: var(--pht-text-muted); font-size: .78rem; }

.pht-empty-notice { color: var(--pht-text-muted); padding: 2rem 0; text-align: center; }

/* ==== history tree (home page) ====
   Modelled on the client's own original hand-built product-history graphic
   (laserworld.com/en/laserworld-product-history.html, a flattened image
   called "historienbaum" - German for "history tree"): one continuous
   vertical lane per product series, product photos placed at the year they
   launched, newest years at the top. Rebuilt here as a real CSS Grid instead
   of a static image - every product is a real, clickable, translated link,
   and it covers the component's entire real date range in one continuous
   grid rather than being split across several separately-authored images. */
.pht-histotree-hint { color: var(--pht-text-muted); margin: -.5rem 0 1.5rem; max-width: 640px; }

.pht-histotree-scroller {
    /* No border/rounded-panel/distinct background any more - the client
       specifically didn't want this reading as a boxed-in embedded widget,
       so it now sits directly on the page's own background like any other
       section, with just a slim, theme-coloured scrollbar (styled below)
       instead of a chunky native one - the actual scroll behaviour hasn't
       changed, only how "obviously separate" it looks. */
    overflow-x: auto;
    /* NOT overflow-y: visible - per the CSS overflow spec, a "visible" value
       on one axis is silently forced to "auto" the moment the OTHER axis is
       anything but visible, so that never actually did anything (confirmed
       via getComputedStyle: this always rendered as overflow-y: auto, one
       stray pixel away from popping up its own redundant inner vertical
       scrollbar next to the page's own). This element's height already
       always auto-sizes to fit its content - nothing about it needs to
       scroll vertically on its own, only sideways - so hidden is both safe
       (never actually clips anything real) and correct (guarantees it can
       never grow a second, unnecessary vertical scrollbar of its own; the
       page underneath already scrolls to reach the rest of the grid). */
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: thin;
    scrollbar-color: var(--pht-border) transparent;
}

.pht-histotree-scroller::-webkit-scrollbar { height: 8px; }
.pht-histotree-scroller::-webkit-scrollbar-track { background: transparent; }
.pht-histotree-scroller::-webkit-scrollbar-thumb { background: var(--pht-border); border-radius: 999px; }
.pht-histotree-scroller::-webkit-scrollbar-thumb:hover { background: var(--pht-accent); }

.pht-histotree {
    /* Slim lanes with a rotated header label, rather than wide lanes with a
       horizontal one - trades header height (paid once) for a lot more
       lanes fitting on screen at once (paid per-lane), so it's a clear net
       space win once there are more than a handful of lanes, which there
       always are here (31 in the real dataset). */
    --pht-lane-w: 2.6rem;
    /* Just wide enough for 4 digits ("2007"-"2026") plus --pht-yearlabel-pad
       on each side - trimmed from 4.5rem, which had more slack than the
       actual year text ever needed. */
    --pht-yearlabel-w: 2.75rem;
    display: grid;
    grid-template-columns: var(--pht-yearlabel-w) repeat(var(--pht-lane-count), var(--pht-lane-w));
}

.pht-histotree-corner {
    position: sticky;
    top: 0;
    left: 0;
    z-index: 3;
    background: var(--pht-bg);
}

.pht-histotree-header {
    /* min-height, not height: a longer series name needs a taller rotated
       label than a shorter one, and a fixed height either clips it or
       crowds it against whatever sits above the grid. Grid items stretch to
       fill their row by default, so every header in the row still ends up
       the same height regardless (set by whichever lane's name is longest)
       - this just lets that shared height grow on its own as names change,
       instead of needing to keep guessing a fixed number by hand. */
    position: sticky;
    top: 0;
    z-index: 2;
    min-height: 10rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-end;
    gap: .5rem;
    padding: .6rem 0 .5rem;
    overflow: hidden;
    background: var(--pht-bg);
    text-align: center;
    text-decoration: none !important;
    color: var(--pht-text) !important;
    transition: background .15s ease;
}

.pht-histotree-header:hover { background: var(--pht-card-hover); }

.pht-histotree-header .dot {
    flex: 0 0 auto;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--lane-color);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--lane-color) 22%, transparent);
}

/* Rotated column heading: vertical-rl + a 180deg flip reads bottom-to-top
   (tilt your head left), the conventional direction for a vertical axis
   label - this is what actually lets the lane itself go this narrow. */
.pht-histotree-header .name {
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    white-space: nowrap;
    font-size: .68rem;
    font-weight: 700;
    letter-spacing: .02em;
}

.pht-histotree-yearlabel {
    position: sticky;
    left: 0;
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    padding: 0 .35rem;
    background: var(--pht-bg);
    border-right: 1px solid var(--pht-border);
    color: var(--pht-text-muted);
    font-size: .85rem;
    font-weight: 600;
    letter-spacing: .05em;
    white-space: nowrap;
}

.pht-histotree-cell {
    position: relative;
    min-height: 4.75rem;
    border-left: 1px solid var(--pht-border);
    border-top: 1px dashed var(--pht-border);
    display: flex;
    flex-wrap: wrap;
    align-content: center;
    justify-content: center;
    gap: .25rem;
    /* Trimmed from .3rem, then .15rem, down to a hairline - each step paired
       with a smaller --pht-lane-w above, to fit more lanes without shrinking
       the thumbnail itself, only the breathing room to its left/right. This
       is close to the practical floor for that approach: the thumbnail
       (2.4rem) plus its 1px border already accounts for almost the whole
       lane width at this point. */
    padding: .04rem;
}

/* The continuous "pole": a coloured strip through the middle of every active
   cell in a lane - consecutive active cells line up into one solid bar
   running from a series' launch year down to wherever it ends (or to the
   top, if it's still current). */
.pht-histotree-cell.is-active::before {
    content: "";
    position: absolute;
    left: 50%;
    top: -1px;
    bottom: -1px;
    width: 4px;
    background: var(--lane-color);
    opacity: .35;
    transform: translateX(-50%);
    pointer-events: none;
}

/* One preview image per cell - anything else that shares this lane+year only
   ever appears in the flyout (see initHistoryTreeFlyout() in site.js). */
.pht-histotree-preview {
    position: relative;
    z-index: 1;
    display: block;
    width: 2.4rem;
    height: 2.4rem;
    border-radius: 8px;
    /* White, not the dark theme colour: not every source photo actually has
       a transparent background (some are a flat white square), and a
       mix-blend-mode trick tried here first made every photo look far too
       dark - multiply darkens every pixel toward a near-black backdrop, not
       just the white ones. A plain white background behind all of them
       looks uniform regardless of which photos are truly transparent - a
       deliberate trade against the darker theme, thumbnails only. */
    background: #fff;
    border: 1px solid var(--pht-border);
    overflow: hidden;
    transition: transform .15s ease, border-color .15s ease, box-shadow .15s ease;
}

.pht-histotree-preview img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    padding: 3px;
    opacity: 0;
    transition: opacity .35s ease;
}

.pht-histotree-preview img.pht-img-loaded { opacity: 1; }
.pht-histotree-preview .no-image { display: block; width: 100%; height: 100%; background: linear-gradient(135deg, #e4e4e8, #f6f6f8); }

.pht-histotree-preview:hover,
.pht-histotree-preview:focus-visible {
    transform: translateY(-2px) scale(1.08);
    border-color: var(--lane-color);
    box-shadow: 0 6px 16px rgba(0,0,0,.4);
}

.pht-histotree-preview[data-has-more] { cursor: pointer; }

.pht-histotree-preview .count-badge {
    position: absolute;
    right: -4px;
    bottom: -4px;
    min-width: 1.05rem;
    height: 1.05rem;
    padding: 0 .25rem;
    border-radius: 999px;
    background: var(--lane-color);
    color: #06121a;
    font-size: .58rem;
    font-weight: 800;
    line-height: 1.05rem;
    text-align: center;
    box-shadow: 0 0 0 2px var(--pht-card);
}

/* The hidden per-cell product list JS reads from to populate the flyout -
   never shown in place, only ever cloned into .pht-histotree-flyout. */
.pht-histotree-popover-content { display: none; }

/* The shared floating "bigger overview" panel - one instance, created once
   by JS and appended to <body> (never an iframe, never boxed in its own
   scroll frame), repositioned per hover/tap. Fixed positioning means it
   escapes the grid's own horizontal-scroll clipping and this page's own
   layout entirely, so it can render whole above anything meant to constrain
   it. */
.pht-histotree-flyout {
    position: fixed;
    z-index: 1000;
    /* width:max-content (bounded by max-width) rather than display:grid's
       auto-fill/auto-fit - a fixed-position, shrink-to-fit box has no
       definite width for grid to base a repeat-column-count on, so a grid
       here would collapse to a single, oddly narrow column. Flex-wrap
       handles "shrink to fit the content, but wrap once you hit max-width"
       correctly without needing a predefined width at all. */
    width: max-content;
    max-width: 420px;
    max-height: 80vh;
    overflow-y: auto;
    display: flex;
    flex-wrap: wrap;
    gap: .6rem;
    padding: .9rem;
    background: var(--pht-card);
    border: 1px solid var(--pht-border);
    border-radius: var(--pht-radius);
    box-shadow: 0 16px 40px rgba(0,0,0,.55);
    opacity: 0;
    transform: scale(.96);
    transform-origin: var(--pht-flyout-origin-x, 0) var(--pht-flyout-origin-y, 0);
    transition: opacity .16s ease, transform .16s ease;
    pointer-events: none;
}

.pht-histotree-flyout.is-open { opacity: 1; transform: scale(1); pointer-events: auto; }

.pht-histotree-flyout-item {
    flex: 0 0 4.5rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .35rem;
    text-align: center;
    color: var(--pht-text) !important;
    text-decoration: none !important;
}

.pht-histotree-flyout-item img,
.pht-histotree-flyout-item .no-image {
    width: 4.5rem;
    height: 4.5rem;
    object-fit: contain;
    border-radius: 8px;
    background: #fff; /* see .pht-histotree-preview for why (white, not the dark theme colour) */
    border: 1px solid var(--pht-border);
    padding: 4px;
    transition: border-color .15s ease, transform .15s ease;
}

.pht-histotree-flyout-item:hover img,
.pht-histotree-flyout-item:focus-visible img { border-color: var(--pht-accent); transform: translateY(-2px); }

.pht-histotree-flyout-item .label { font-size: .72rem; color: var(--pht-text-muted); line-height: 1.25; }

/* Hover-trace: lightly highlight the header/year-label matching whatever
   cell the pointer is currently over, so it's easy to read off "which lane,
   which year" at a glance while moving across the grid. */
.pht-histotree-header.is-traced,
.pht-histotree-yearlabel.is-traced {
    background: var(--pht-card-hover);
    color: var(--pht-text);
}

.pht-histotree-header.is-traced { color: var(--lane-color) !important; }

/* Scroll-reveal, per year-row rather than per cell (hundreds of individual
   cells would be needlessly expensive to observe) - progressively enhanced,
   see initHistoryTreeRowReveal() in site.js; base state is fully visible so
   a browser without IntersectionObserver just shows a plain static grid. */
.pht-histotree.pht-rows-ready [data-pht-row] {
    opacity: 0;
    transform: translateY(18px);
    transition: opacity .5s ease, transform .5s ease;
}

.pht-histotree.pht-rows-ready [data-pht-row].is-visible {
    opacity: 1;
    transform: none;
}

.pht-histotree-scroll-hint {
    display: none;
    color: var(--pht-text-muted);
    font-size: .82rem;
    margin: .6rem 0 0;
    text-align: right;
}

@media (prefers-reduced-motion: reduce) {
    .pht-histotree-preview:hover,
    .pht-histotree-preview:focus-visible { transform: none; }
    .pht-histotree-flyout { transition: none; }
    .pht-histotree.pht-rows-ready [data-pht-row] { opacity: 1; transform: none; transition: none; }
}

/* Mobile: same grid (a dense multi-lane timeline is inherently a wide,
   data-heavy view - horizontal scroll is the expected, standard pattern for
   this kind of chart even on phones, same as it would be for any spreadsheet
   or Gantt view), just narrower lanes/cells and an explicit scroll hint. */
@media (max-width: 767.98px) {
    .pht-histotree { --pht-lane-w: 2.5rem; }
    .pht-histotree-cell { min-height: 3rem; }
    .pht-histotree-preview { width: 1.9rem; height: 1.9rem; }
    .pht-histotree-header { min-height: 8rem; }
    .pht-histotree-header .name { font-size: .6rem; }
    .pht-histotree-scroll-hint { display: block; }
    .pht-histotree-flyout { max-width: 90vw; }
}
