/* ===========================================================================
   UTILITY TOOLBOX — section styling.

   Framework pass: the title bar and grid are fully built and working, the
   grid itself just has "coming soon" stand-ins in it until real tools land.
   Layout numbers (gap, columns) are kept identical to CE-styling.css on
   purpose, so the two sections read as siblings rather than two different
   sites.
   =========================================================================== */

:root {
    --browser-gap: 8px;
}

/* ---- Title bar ----
   A real toolbar rather than a full-bleed poster like CE's: the art here is
   borrowed from Creative Experiments as a placeholder (see the HTML comment
   above the <img>) and will not always be a wide banner, so the layout can't
   assume it fills the whole bar. Image on the left, filters on the right,
   both sitting on a plain panel. */
#UT-title-bar {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin: var(--browser-gap);
    margin-bottom: 0;
    padding: 12px;
    background: var(--color-primary-a10);
    border-radius: 8px;
    box-sizing: border-box;
}

.UT-title-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 16px;
}

.UT-description {
    margin: 0;
    padding: 0 4px;
    font-size: 13px;
    line-height: 1.6;
    color: var(--color-text-2);
    font-family: "Circular Spotify", "Inter", sans-serif;
}

.UT-title-image {
    flex-shrink: 0;
    height: clamp(48px, 9.6vw, 128px);
    aspect-ratio: 2246 / 512;
    border-radius: 6px;
    overflow: hidden;
    display: block;
}

.UT-title-image img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.UT-filter-bar {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    justify-content: flex-end;
    margin-left: auto;
}

.UT-filter-chip {
    position: relative;
    isolation: isolate; /* keeps ::before's z-index:-1 contained — see the note on it below */
    font-family: "Circular Spotify", "Inter", sans-serif;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.04em;
    padding: 8px 18px;
    border-radius: 24px;
    border: none;
    background: var(--color-primary-a20);
    color: var(--color-text-2);
    cursor: pointer;
    transition:
        background-color var(--dur-hover) var(--ease-soft),
        color var(--dur-hover) var(--ease-soft),
        transform var(--dur-hover) var(--ease-pop),
        box-shadow var(--dur-hover) var(--ease-soft);
}

/* Each category carries its own accent — Godot's is the engine's own blue,
   the other two just needed to read distinctly from it and from each
   other. "All" deliberately has none: it isn't a category, so it keeps the
   site's ordinary selected-blue gradient instead of inventing a colour
   that wouldn't mean anything. The tag pills on the cards below reuse
   these same values, so a colour means the same thing in both places. */
.UT-filter-chip[data-filter="online"] {
    --chip-accent: #2dd4bf;
    --chip-tint: rgba(45, 212, 191, 0.16);
    --chip-tint-hover: rgba(45, 212, 191, 0.28);
    --chip-ring: rgba(45, 212, 191, 0.4);
}

.UT-filter-chip[data-filter="godot"] {
    --chip-accent: #478cbf; /* same blue as the Godot tool tag elsewhere on the site */
    --chip-tint: rgba(71, 140, 191, 0.16);
    --chip-tint-hover: rgba(71, 140, 191, 0.28);
    --chip-ring: rgba(71, 140, 191, 0.4);
}

.UT-filter-chip[data-filter="desktop"] {
    --chip-accent: #d9822b;
    --chip-tint: rgba(217, 130, 43, 0.16);
    --chip-tint-hover: rgba(217, 130, 43, 0.28);
    --chip-ring: rgba(217, 130, 43, 0.4);
}

/* A quiet tint and ring at rest, so the row is scannable by colour before
   a reader clicks anything, without shouting as loud as the active state
   does. */
.UT-filter-chip[data-filter]:not([data-filter="all"]) {
    background: var(--chip-tint);
    box-shadow: inset 0 0 0 1.5px var(--chip-ring);
}

@media (hover: hover) {
    .UT-filter-chip:hover {
        background: var(--color-primary-a30);
        transform: translateY(-2px);
    }

    .UT-filter-chip[data-filter]:not([data-filter="all"]):hover {
        background: var(--chip-tint-hover);
        transform: translateY(-2px);
    }
}

.UT-filter-chip:active {
    transform: translateY(0) scale(0.95);
    transition-duration: var(--dur-press);
}

/* The full-colour active fill is a separate layer that fades in over the
   resting tint, rather than the chip's own background swapping outright.
   A gradient is a background IMAGE — it can't be interpolated, so animating
   it directly would snap instead of cross-fade (the same reasoning as the
   title-card gradient in CE-styling.css). Opacity does interpolate, so the
   fill sits on its own pseudo-element and only its opacity moves. */
.UT-filter-chip::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    border-radius: inherit;
    background: var(--chip-accent, linear-gradient(135deg, var(--color-selected-1) 0%, var(--color-selected-2) 100%));
    opacity: 0;
    transition: opacity var(--dur-hover) var(--ease-soft);
}

.UT-filter-chip.is-active {
    color: var(--color-text-1);
    box-shadow: var(--shadow-card-hover);
}

.UT-filter-chip.is-active::before {
    opacity: 1;
}

/* Below 640px the image and the filters no longer have room to sit side by
   side, so instead of just letting them shrink and rattle around in the box
   with empty space either side, both stretch out to the full width of the
   bar — a banner up top, a row of even-width pills under it. */
@media (max-width: 640px) {
    .UT-title-image {
        width: 100%;
        height: auto;
    }

    .UT-filter-bar {
        justify-content: stretch;
        margin-left: 0;
        width: 100%;
    }

    .UT-filter-chip {
        flex: 1 1 0;
        text-align: center;
        padding-left: 8px;
        padding-right: 8px;
    }
}

/* ---- Live tool cards ----
   Every live card uses .has-cover with a real <img>, the same as Creative
   Experiments, and a tool with no art of its own yet points that <img> at
   UT-Placeholder.svg. .is-live is kept for a card that wants no picture at
   all: a quiet gradient panel rather than the dashed .is-placeholder frame.
   Its hover and active treatment is written alongside .has-cover further
   down, so this is only the resting look. */
.UT-browser-item.is-live {
    background:
        linear-gradient(150deg, var(--color-primary-a10) 0%, var(--color-primary-a20) 100%);
    border: 1px solid var(--color-card-border);
    cursor: pointer;
}

.UT-browser-item.is-live .UT-item-title {
    /* No cover art underneath, so the title needs no scrim to sit on. */
    color: var(--color-text-1);
}

/* ---- Tool grid ----
   Same numbers as .CE-browser-grid so the two sections line up when a reader
   moves between them, kept as its own class so the two can drift apart later
   without one styling file having to know about the other.

   auto-fill, not auto-fit. CE never has this problem because it never has
   fewer items than it started with, but this grid does the moment a filter
   is applied. auto-fit deletes empty tracks and hands their space to
   whichever cards are left, so filtering down to one card let it stretch to
   the full width of the grid. auto-fill keeps the empty tracks in place —
   sized, just uninhabited — so the remaining cards stay the size every card
   already is, and the row simply ends early instead of one tile swallowing
   the gap. */
.UT-browser-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 250px), 1fr));
    gap: var(--browser-gap);
    margin: var(--browser-gap);
}

@media (min-width: 1100px) {
    .UT-browser-grid {
        grid-template-columns: repeat(4, 1fr);
    }
}

@media (max-width: 640px) {
    .UT-browser-grid {
        margin-bottom: 100px;
    }
}

.UT-browser-item {
    aspect-ratio: 1 / 1;
    box-sizing: border-box;
    border-radius: 8px;
    text-decoration: none;
    position: relative;
    overflow: hidden;
    /* Makes the card its own stacking context, so the scrim / text / glint
       z-indexes further down are ordered against each other and nothing
       else in the grid. */
    isolation: isolate;

    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    padding: 24px;
}

/* The opacity/transform transitions specifically need to out-rank the
   scroll-reveal system's own .js-motion .reveal-armed rule in
   CSS/fixed-styling.css — at one class each, plain .UT-browser-item
   (0,1,0) loses to its two (0,2,0), and would silently sit at whatever
   duration THAT rule declares instead of var(--dur-panel) below. Tying it
   at two classes and letting this file's later position in <head> break
   the tie is enough; see the same reasoning again on .is-hiding further
   down for the value itself.

   --ease-out here, not --ease-soft: this is what plays when a card is
   ARRIVING back into the grid after a filter change (.is-hiding is being
   removed), and --ease-out is documented as the site's own curve for
   exactly that in CSS/fixed-styling.css. The reverse direction — a card
   LEAVING — gets its own transition declared on .is-hiding itself, using
   --ease-in instead, because a single transition can't play two different
   curves depending on which way a value is headed. */
.UT-browser-grid .UT-browser-item {
    transition:
        opacity var(--dur-panel) var(--ease-out),
        transform var(--dur-panel) var(--ease-out),
        box-shadow var(--dur-hover) var(--ease-out);
}

.UT-item-title {
    position: relative;
    color: var(--color-text-1);
    margin: 0 0 12px;
    font-size: clamp(18px, 3vw, 28px);
    line-height: 1.05;
    font-weight: 600;
    font-family: "Hanson", "Circular Spotify", "Inter";
}

.UT-item-tag {
    position: relative;
    display: inline-flex;
    align-items: center;
    width: fit-content;
    padding: 6px 14px;
    border-radius: 24px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    font-family: "Circular Spotify", "Inter", sans-serif;
    background: var(--tag-accent, var(--color-primary-a30));
    color: var(--color-text-1);
}

/* Same three accents as the filter chips above, so a card's tag and the
   chip that filters it to are the same colour. */
.UT-item-tag[data-tag="online"]  { --tag-accent: #2dd4bf; }
.UT-item-tag[data-tag="godot"]   { --tag-accent: #478cbf; } /* same Godot accent used on the CE tool tags */
.UT-item-tag[data-tag="desktop"] { --tag-accent: #d9822b; }

/* ── Real tool cards ───────────────────────────────────────────────────
   Cover art plus the site's glint treatment.

   THE SCRIM IS ON THE CARD, NOT ON THE TITLE. This started life copied
   straight from .CE-browser-item, where the gradient sits on the title's own
   background with margin: -24px to bleed it back out past the card's padding.
   That works there because a CE card holds one element. A toolbox card holds
   two — a title and a category tag under it — and the negative margin also
   cancels the title's `margin: 0 0 12px`, so the gradient ran 24px down over
   the pill, the gap between the two closed up, and the bottom-left radius was
   rounding a corner the gradient was nowhere near.

   A scrim across the bottom of the card instead sits behind both of them, so
   the title and tag keep their ordinary spacing and the gradient lines up
   with the card's own edges. */
.UT-browser-item.has-cover {
    background: #3e3e3e;
}

.UT-browser-thumb {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: contain;
    transition: transform var(--dur-panel) var(--ease-out);
}

.UT-browser-item.has-cover::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 62%;
    background: linear-gradient(to top,
        rgba(0, 0, 0, 0.78) 0%,
        rgba(0, 0, 0, 0.45) 45%,
        rgba(0, 0, 0, 0) 100%);
    pointer-events: none;
    z-index: 1;
}

/* The three layers the card stacks, named in one place because the order is
   not the DOM order: ::before is generated as the FIRST child, so without a
   z-index the scrim would paint underneath the cover art it is meant to sit
   on. .UT-browser-item's `isolation: isolate` keeps all of this contained to
   the card rather than competing with the rest of the grid. */
.UT-browser-item.has-cover .UT-item-title,
.UT-browser-item.has-cover .UT-item-tag {
    z-index: 2;
}

/* Fixed white rather than --color-text-1. This title sits on cover art and a
   dark scrim whichever theme is on, so following the theme's own text colour
   turned it near-black on a dark picture in light mode. .CE-browser-item p
   hard-codes white for the same reason, as does the title card's eyebrow
   further up. The shadow is for the cards that end up with pale artwork. */
.UT-browser-item.has-cover .UT-item-title {
    color: #ffffff;
    text-shadow: 0 2px 6px rgba(0, 0, 0, 0.6);
}

.UT-browser-item::after {
    content: "";
    position: absolute;
    top: -50%;
    left: -75%;
    width: 50%;
    height: 200%;
    background: linear-gradient(120deg, transparent 0%, rgba(255, 255, 255, 0.25) 50%, transparent 100%);
    transform: rotate(25deg);
    opacity: 0;
    pointer-events: none;
    z-index: 3; /* the glint sweeps over the text, not behind it */
}

@media (hover: hover) {
    .UT-browser-item.has-cover:hover,
    .UT-browser-item.is-live:hover {
        transform: translateY(-4px) scale(1.02);
        box-shadow: 0 10px 24px rgba(0, 0, 0, 0.5);
        /* The base rule's transform duration is var(--dur-panel), tuned for
           the slower filter collapse/arrive above — a hover lift at that
           speed would feel sluggish, so this puts it back to the snappier
           var(--dur-hover) every other hoverable card on the site uses. */
        transition-duration: var(--dur-hover);
    }

    .UT-browser-item.has-cover:hover .UT-browser-thumb {
        transform: scale(1.05);
    }

    .UT-browser-item.has-cover:hover::after,
    .UT-browser-item.is-live:hover::after {
        animation: glint-sweep 0.8s ease forwards;
    }
}

.UT-browser-item.has-cover:active,
.UT-browser-item.is-live:active {
    transform: scale(0.99);
    transition-duration: var(--dur-press);
}

/* ── Placeholders ────────────────────────────────────────────────────
   A dashed frame reads as "nothing here yet" rather than "broken image",
   and dropping the hover lift keeps a reader from expecting a click that
   does nothing. */
.UT-browser-item.is-placeholder {
    background: var(--color-primary-a10);
    border: 2px dashed var(--color-primary-a20);
    cursor: default;
}

.UT-browser-item.is-placeholder .UT-item-title {
    color: var(--color-text-2);
}

.UT-soon-badge {
    position: absolute;
    top: 16px;
    left: 16px;
    padding: 4px 10px;
    border-radius: 24px;
    background: var(--color-primary-a20);
    color: var(--color-primary-a30);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    font-family: "Circular Spotify", "Inter", sans-serif;
}

/* ---- Filtering ----
   Two things had to be true at once here, and they pull in opposite
   directions: outgoing cards need to actually leave the grid — collapsing
   away rather than leaving a permanent gap behind — but a reflow is not
   something a transition can soften, so anything still visibly moving or
   fading at the moment that reflow happens reads as a jump, not a shrink.

   The fix is sequencing, owned by the script at the bottom of the page.
   .is-hiding plays first, on its own, and only shrinks/fades a card in
   place — it does NOT touch layout, so nothing reflows yet. Once every
   outgoing card has actually finished that (not a moment before), the
   script adds .is-filtered-out to all of them in the same tick, so the
   grid only repacks once, in a single beat, with nothing else mid-animation
   to make that beat look like a glitch. Only after that does it bring the
   newly-matching cards in — same shrink/fade, reversed.

   .UT-browser-grid prefixed on .is-hiding for the same reason as the
   transition above: once the scroll-reveal system has taken charge of a
   card, .js-motion .reveal-armed.is-revealed (0,3,0) is holding
   opacity: 1, and plain .UT-browser-item.is-hiding (0,2,0) is not specific
   enough to override it — the card would carry the class but never
   actually animate. */
.UT-browser-grid .UT-browser-item.is-hiding {
    opacity: 0;
    transform: scale(0.92);
    pointer-events: none;
    /* --ease-in, not the arriving --ease-out the base rule plays — this is
       the one direction a card is ever LEAVING under its own steam rather
       than settling into place, which CSS/fixed-styling.css documents as
       --ease-in's job. */
    transition:
        opacity var(--dur-panel) var(--ease-in),
        transform var(--dur-panel) var(--ease-in);
}

.UT-browser-item.is-filtered-out {
    display: none;
}

/* ===========================================================================
   TOOL PAGES — the shell an individual tool sits in.

   Creative Experiments keeps the equivalent pieces (.project-content-div, its
   back button, .ah-section-divider and so on) in CE-styling.css, and that file
   is only loaded on CE pages — so a Utility Toolbox tool page cannot borrow
   them. These are the same ideas rebuilt against the same tokens, kept here
   rather than in a per-tool stylesheet so the second tool page only has to
   write the tool.

   Reveal-on-scroll is opted into with data-reveal on the markup, which is
   already in both the CSS selector list (CSS/fixed-styling.css) and its mirror
   in js_main.js — nothing here needs adding to either.
   =========================================================================== */

.UT-tool-page {
    padding: 24px;
}

.UT-tool-page #page-header {
    line-height: 1em;
    margin-bottom: 12px;
}

@media (max-width: 640px) {
    .UT-tool-page {
        padding: 16px;
        padding-bottom: 100px;
    }
}

/* ---- Back to the toolbox ----
   CE's back button is a piece of cover art; there is no equivalent picture for
   this section yet, so it is a pill that says where it goes. Same movement as
   CE's on hover — back, so it moves back. */
.UT-back-button {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 24px;
    padding: 10px 18px 10px 14px;
    border-radius: 24px;
    background: var(--color-primary-a10);
    border: 1px solid var(--color-card-border);
    color: var(--color-text-2);
    font-family: "Circular Spotify", "Inter", sans-serif;
    font-size: 13px;
    font-weight: 600;
    text-decoration: none;
    width: fit-content;
    transition:
        transform var(--dur-hover) var(--ease-pop),
        background-color var(--dur-hover) var(--ease-soft),
        color var(--dur-hover) var(--ease-soft),
        box-shadow var(--dur-hover) var(--ease-out);
}

/* style_main.css sets a:link / a:visited at (0,1,1), and it is loaded after
   this file — a plain .UT-back-button at (0,1,0) loses, so the pill came out
   link-blue, which in light mode is a pale blue on a pale grey. Matching the
   link pseudo-classes puts it back to the button's own colour. */
a.UT-back-button:link,
a.UT-back-button:visited {
    color: var(--color-text-2);
}

.UT-back-button::before {
    content: "←";
    font-size: 15px;
    line-height: 1;
    transition: transform var(--dur-hover) var(--ease-pop);
}

@media (hover: hover) {
    a.UT-back-button:hover {
        transform: translate(-4px, -2px);
        background: var(--color-primary-a20);
        color: var(--color-text-1);
        box-shadow: var(--shadow-card-hover);
    }

    .UT-back-button:hover::before {
        transform: translateX(-3px);
    }
}

.UT-back-button:active {
    transform: translateX(-2px) scale(0.98);
    transition-duration: var(--dur-press);
}

/* ---- Title card ----
   The one deliberately loud panel on a tool page: the site's selected-blue
   gradient, the tool's art on the left and its name and summary on the right.
   Plain gradient, no texture over the top. */
.UT-tool-hero {
    position: relative;
    overflow: hidden;
    border-radius: 16px;
    padding: 28px;
    margin-bottom: 24px;
    background: linear-gradient(135deg, var(--color-selected-1) 0%, var(--color-selected-2) 100%);
    box-shadow: var(--shadow-card-hover);
}

.UT-tool-hero-row {
    display: flex;
    align-items: center;
    gap: 28px;
}

@media (max-width: 720px) {
    .UT-tool-hero {
        padding: 20px;
    }

    .UT-tool-hero-row {
        flex-direction: column;
        align-items: flex-start;
        gap: 18px;
    }
}

.UT-tool-hero-art {
    flex-shrink: 0;
    width: clamp(96px, 18vw, 156px);
    filter: drop-shadow(0 6px 14px rgba(0, 0, 0, 0.45));
}

.UT-tool-hero-art svg {
    display: block;
    width: 100%;
    height: auto;
}

.UT-tool-hero-eyebrow {
    display: block;
    font-family: "Circular Spotify", "Inter", sans-serif;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    /* Fixed white rather than --color-text-1: the card keeps its blue in both
       themes, so text on it cannot follow the theme's own text colour or it
       turns near-black on a mid blue in light mode. */
    color: rgba(255, 255, 255, 0.75);
    margin-bottom: 8px;
}

.UT-tool-hero h2 {
    margin: 0 0 10px;
    font-family: "Hanson", "Circular Spotify", "Inter";
    font-size: clamp(24px, 4vw, 40px);
    line-height: 1.05;
    color: #ffffff;
}

.UT-tool-hero p {
    margin: 0;
    max-width: 62ch;
    font-size: 14px;
    line-height: 1.65;
    color: rgba(255, 255, 255, 0.88);
}

.UT-tool-hero code {
    padding: 2px 6px;
    border-radius: 4px;
    background: rgba(0, 0, 0, 0.28);
    color: #ffffff;
    font-family: "JetBrains Mono", "Fira Mono", "Consolas", ui-monospace, monospace;
    font-size: 13px;
}

/* ---- Tool / platform tags ---- */

.UT-tools-row {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 18px;
}

.UT-tool-tag {
    display: inline-flex;
    align-items: center;
    padding: 7px 15px;
    border-radius: 24px;
    font-family: "Circular Spotify", "Inter", sans-serif;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    background: var(--tag-accent, rgba(0, 0, 0, 0.28));
    color: #ffffff;
}

/* Same three accents as the filter chips and card tags above. */
.UT-tool-tag[data-tool="online"]  { --tag-accent: #2dd4bf; color: #05302b; }
.UT-tool-tag[data-tool="godot"]   { --tag-accent: #478cbf; }
.UT-tool-tag[data-tool="desktop"] { --tag-accent: #d9822b; }

/* ---- Section dividers ---- */

.UT-section-divider {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 32px 0 16px;
}

.UT-section-divider::before,
.UT-section-divider::after {
    content: "";
    flex: 1;
    height: 2px;
    background: var(--color-primary-a20);
}

.UT-section-divider span {
    font-family: "Circular Spotify", "Inter", sans-serif;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--color-primary-a30);
    white-space: nowrap;
}

/* ---- "How it works" steps ----
   An ordered list, because the order is the point. The numbers are drawn from
   the list counter rather than typed into the markup so a step can be inserted
   without renumbering the rest. */
.UT-steps {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
    gap: 12px;
    margin: 0;
    padding: 0;
    list-style: none;
    counter-reset: ut-step;
}

.UT-steps li {
    counter-increment: ut-step;
    position: relative;
    padding: 16px 16px 16px 52px;
    border-radius: 16px;
    background: var(--color-primary-a10);
    font-family: "Circular Spotify", "Inter", sans-serif;
    font-size: 13.5px;
    line-height: 1.6;
    color: var(--color-text-2);
}

.UT-steps li::before {
    content: counter(ut-step);
    position: absolute;
    top: 16px;
    left: 16px;
    width: 24px;
    height: 24px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: var(--color-selected-2);
    color: #ffffff;
    font-size: 12px;
    font-weight: 700;
}

.UT-steps strong {
    display: block;
    color: var(--color-text-1);
    font-weight: 700;
    margin-bottom: 2px;
}

/* ---- Notes / prose blocks ---- */

.UT-note {
    margin: 0 0 16px;
    padding: 14px 16px;
    border-radius: 16px;
    background: var(--color-primary-a10);
    border-left: 3px solid var(--color-selected-2);
    font-family: "Circular Spotify", "Inter", sans-serif;
    font-size: 13.5px;
    line-height: 1.65;
    color: var(--color-text-2);
}

.UT-note strong {
    color: var(--color-text-1);
}

.UT-tool-page .UT-prose {
    font-size: 14px;
    line-height: 1.7;
    color: var(--color-text-2);
    font-family: "Circular Spotify", "Inter", sans-serif;
}

.UT-tool-page .UT-prose code {
    padding: 2px 6px;
    border-radius: 4px;
    background: var(--color-primary-a10);
    color: var(--color-text-1);
    font-family: "JetBrains Mono", "Fira Mono", "Consolas", ui-monospace, monospace;
    font-size: 13px;
}

/* ---- Licence / credits footer ----
   The same block CE-styling.css carries, repeated here for the same reason as
   everything else in this section: that file is not loaded on these pages.
   .license-group is already in the scroll-reveal selector list, so the markup
   only has to exist. */

.license-footer {
    margin-top: 8px;
    margin-bottom: 8px;
}

.license-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
    gap: 12px;
    margin-bottom: 16px;
}

.license-group {
    background: var(--color-primary-a10);
    border-radius: 16px;
    padding: 16px;
}

.license-group-label {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-primary-a30);
    margin: 0 0 12px;
    font-family: "Circular Spotify", "Inter", sans-serif;
}

.license-item {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 8px 0;
    border-bottom: 1px solid var(--color-primary-a10);
}

.license-item:first-of-type {
    padding-top: 0;
}

.license-item:last-child {
    padding-bottom: 0;
    border-bottom: none;
}

.license-asset {
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text-1);
    font-family: "Circular Spotify", "Inter", sans-serif;
}

.license-detail {
    font-size: 12px;
    line-height: 1.5;
    color: var(--color-text-2);
    opacity: 0.75;
    font-family: "Circular Spotify", "Inter", sans-serif;
}

/* Credits that link out to the thing they are crediting.

   Not decoration: the LGPL libraries this site serves (libav.js, lamejs) ask
   that whoever receives the library can get at its source, and naming the
   author only covers the notice half of that. A link on the name is what
   discharges the rest, so it has to read as a link rather than be styled into
   the surrounding text.

   The underline is a border rather than text-decoration so it clears the
   descender on a "j" at this size. */
/* :is(:link, :visited) rather than a bare `a` on purpose. style_main.css sets
   `a:link` and `a:visited` globally and loads after this file, so a plain
   `.license-asset a` ties on specificity and loses on order — and the global
   visited colour is a bright pink that would turn half the credits list a
   different colour from the other half once somebody had clicked through. */
.license-asset a:is(:link, :visited) {
    color: var(--color-accent-text);
    text-decoration: none;
    border-bottom: 1px solid color-mix(in srgb, var(--color-accent-text) 40%, transparent);
    transition: color 0.15s ease, border-color 0.15s ease;
}

.license-asset a:is(:link, :visited):hover {
    color: var(--color-text-1);
    border-bottom-color: var(--color-text-1);
}

.license-asset a:focus-visible {
    outline: 2px solid var(--color-selected-3);
    outline-offset: 2px;
    border-radius: 2px;
}

/* The outward arrow on a link that leaves the site.

   Drawn as a masked SVG rather than the "97" character it started as.
   Circular Spotify has no glyph at that codepoint, so the browser fell back to
   a tofu box with the codepoint printed inside it — which on a licence credit
   reads as a broken page rather than as a link marker. A mask takes its colour
   from the link via background-color, so it still follows the theme and the
   hover state. Empty content keeps it out of the accessible name. */
.license-asset a[target="_blank"]::after {
    content: "";
    display: inline-block;
    width: 0.62em;
    height: 0.62em;
    margin-left: 4px;
    vertical-align: baseline;
    background-color: currentColor;
    opacity: 0.7;
    -webkit-mask-image: var(--icon-external);
    mask-image: var(--icon-external);
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-size: contain;
    mask-size: contain;
}

.license-footer {
    --icon-external: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 17 17 7'/%3E%3Cpath d='M8 7h9v9'/%3E%3C/svg%3E");
}

/* A second line under the description, for the source and licence links the
   copyleft libraries ask to travel with the code.

   Set quieter than the description above it at first, which was the wrong way
   round: these are the links that actually discharge the LGPL obligation, so
   they have to be comfortably readable rather than decorative. At 0.6 they
   measured 3.75:1 on dark and 3.08:1 on light, both under WCAG AA for text
   this size. Kept a shade below the description so the hierarchy still
   reads. */
.license-source {
    display: block;
    font-size: 11px;
    margin-top: 4px;
    color: var(--color-text-2);
    opacity: 0.85;
    font-family: "Circular Spotify", "Inter", sans-serif;
}

.license-source a:is(:link, :visited) {
    color: inherit;
    text-decoration: underline;
    text-underline-offset: 2px;
    /* These labels are all short ("LGPL-2.1 text", "RFC 8878"), and on a phone
       the line wraps often enough to split one down the middle. The line itself
       still wraps between links, just not through one. */
    white-space: nowrap;
}

.license-source a:is(:link, :visited):hover {
    color: var(--color-text-1);
}

.license-source a:focus-visible {
    outline: 2px solid var(--color-selected-3);
    outline-offset: 2px;
    border-radius: 2px;
}

.license-note {
    font-size: 12px;
    text-align: center;
    color: var(--color-text-2);
    opacity: 0.45;
    font-family: "Circular Spotify", "Inter", sans-serif;
    margin: 0;
    padding-top: 8px;
}
