/* =========================================================================
   blackfullscreen.com — UI components
   Depends on tokens.css. Never hardcode a colour, radius or space here.
   ========================================================================= */

*, *::before, *::after { box-sizing: border-box; }

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

body {
    margin: 0;
    background: var(--ground);
    color: var(--ink);
    font-family: var(--font-ui);
    font-size: var(--t-md);
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
    position: relative;
}

/* Ambient wash tinted by the page's own colour. Very low opacity on purpose:
   it should register as depth, not as a coloured background. This is most of
   what stops a flat white page reading as unfinished. */
body::before {
    content: "";
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 0;
    background:
        radial-gradient(58% 42% at 50% -8%,
            color-mix(in srgb, var(--active) 16%, transparent) 0%, transparent 72%),
        radial-gradient(38% 30% at 92% 4%,
            color-mix(in srgb, var(--active) 8%, transparent) 0%, transparent 70%);
}

/* Fine grain. Kills the flatness of large empty areas without reading as texture. */
body::after {
    content: "";
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 0;
    opacity: .022;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

main, .site-head, .site-foot { position: relative; z-index: 1; }

h1, h2, h3, h4 {
    font-family: var(--font-display);
    font-weight: 700;
    letter-spacing: -.02em;
    line-height: 1.15;
    margin: 0;
    text-wrap: balance;
}

a { color: inherit; }

.wrap {
    max-width: var(--maxw);
    margin: 0 auto;
    padding: 0 var(--sp-5);
}

.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

/* Visible focus everywhere. --active means the ring matches the page colour. */
:where(a, button, input, select, [tabindex]):focus-visible {
    outline: 2px solid var(--active);
    outline-offset: 3px;
    border-radius: var(--r-sm);
}

.sr-only {
    position: absolute; width: 1px; height: 1px;
    padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---------------------------------------------------------------- header */

.site-head {
    position: sticky;
    top: 0;
    z-index: 40;
    background: color-mix(in srgb, var(--ground) 86%, transparent);
    backdrop-filter: blur(12px);
    border-bottom: 1px solid var(--line);
}

.site-head__in {
    max-width: var(--maxw);
    margin: 0 auto;
    padding: var(--sp-3) var(--sp-5);
    display: flex;
    align-items: center;
    gap: var(--sp-5);
    /* Positioning context for the tools dropdown on narrow screens. See the
       720px block under "nav dropdown" for why it cannot anchor to the button
       there, and why position:fixed is not available. */
    position: relative;
}

.brand {
    font-family: var(--font-display);
    font-size: var(--t-lg);
    letter-spacing: -.03em;
    text-decoration: none;
    white-space: nowrap;
}

.site-nav {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: var(--sp-4);
}

.site-nav a {
    font-size: var(--t-sm);
    color: var(--ink-2);
    text-decoration: none;
    transition: color var(--fast) var(--ease);
}

.site-nav a:hover { color: var(--ink); }

/* ------------------------------------------------------------ nav dropdown */

.nav-drop { position: relative; }

.nav-drop__btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font: inherit;
    font-size: var(--t-sm);
    color: var(--ink-2);
    background: none;
    border: 0;
    padding: 6px 0;
    cursor: pointer;
    position: relative;   /* anchors the hover bridge below */
}

/* The 10px gap between the button and the menu belonged to neither element, so
   moving the pointer straight down across it fired mouseleave on .nav-drop and
   closed the menu before the pointer ever arrived. This covers the gap with an
   invisible extension of the button, only while the menu is open. The diagonal
   case, travelling toward the far corner of a 680px menu, is handled by the
   close delay in sitehead.php: the bridge alone cannot cover that path. */
.nav-drop__btn[aria-expanded="true"]::after {
    content: "";
    position: absolute;
    top: 100%;
    left: -24px;
    right: -24px;
    height: 12px;
}
.nav-drop__btn:hover, .nav-drop__btn[aria-expanded="true"] { color: var(--ink); }
.nav-drop__btn svg { transition: transform var(--fast) var(--ease); }
.nav-drop__btn[aria-expanded="true"] svg { transform: rotate(180deg); }

.nav-drop__menu {
    position: absolute;
    top: calc(100% + 10px);
    right: 0;
    z-index: 60;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 16px;
    box-shadow: var(--lift-3), var(--sheen);
    padding: var(--sp-5);
    width: min(680px, calc(100vw - 32px));

    /* 47 colours make this list taller than a laptop viewport, and the header is
       position:sticky, so anything running past the bottom of the screen simply
       cannot be scrolled to. Cap it and let the menu scroll inside itself.
       --head-h is written by sitehead.php from the real header height. */
    max-height: calc(100vh - var(--head-h, 59px) - 24px);
    overflow-y: auto;
    overscroll-behavior: contain;
}

.nav-drop__cols { display: grid; grid-template-columns: 1.1fr 1fr; gap: var(--sp-6); }

.nav-drop__h {
    margin: 0 0 var(--sp-3);
    font-size: 10.5px;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--ink-3);
    font-weight: 700;
}

.nav-drop__col > a {
    display: block;
    text-decoration: none;
    padding: 8px 10px;
    margin: 0 -10px;
    border-radius: var(--r-sm);
    transition: background var(--fast) var(--ease);
}
.nav-drop__col > a:hover { background: var(--sunken); }
.nav-drop__col > a strong { display: block; font-size: var(--t-sm); font-weight: 600; }
.nav-drop__col > a span {
    display: block;
    font-size: 12px;
    color: var(--ink-3);
    line-height: 1.4;
    margin-top: 1px;
}

.nav-drop__swatches {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 2px 10px;
}
.nav-drop__swatches a {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12.5px;
    color: var(--ink-2);
    text-decoration: none;
    padding: 5px 8px;
    margin: 0 -8px;
    border-radius: var(--r-sm);
}
.nav-drop__swatches a:hover { background: var(--sunken); color: var(--ink); }
.nav-drop__swatches a span {
    width: 13px; height: 13px;
    border-radius: 4px;
    flex: none;
    box-shadow: inset 0 0 0 1px rgba(0,0,0,.16);
}

.nav-drop__more {
    display: inline-block;
    margin-top: var(--sp-3);
    font-size: 12.5px;
    font-weight: 600;
    color: var(--ink-2);
    text-decoration: none;
}
.nav-drop__more:hover { color: var(--ink); }

@media (max-width: 720px) {
    /* The menu used to anchor to the button's right edge and extend leftward.
       At 386px that put its left edge at -145px: a third of the menu, including
       the whole first column of colours, sat off the side of the screen and
       could not be reached.

       So on narrow screens the positioning context moves up to .site-head__in
       and the menu spans the full container instead. position:fixed would be the
       obvious alternative and does not work here: .site-head has a
       backdrop-filter, which makes it the containing block for fixed
       descendants, so "fixed" would anchor to the header, not the viewport. */
    .nav-drop { position: static; }

    .nav-drop__menu {
        top: calc(100% + 10px);
        left: 0;
        right: 0;
        width: auto;
        padding: var(--sp-4);
    }

    .nav-drop__cols { grid-template-columns: 1fr; gap: var(--sp-5); }
    .nav-drop__col > a span { display: none; }

    /* Three columns of swatches rather than two: at this width the labels are
       short and it keeps 47 colours from becoming an endless scroll. */
    .nav-drop__swatches { grid-template-columns: repeat(3, 1fr); gap: 2px 6px; }
    .nav-drop__swatches a { font-size: 12px; padding: 6px 6px; gap: 6px; }
}

.theme-toggle {
    display: inline-flex;
    background: var(--sunken);
    border: 1px solid var(--line);
    border-radius: var(--r-pill);
    padding: 2px;
}

.theme-toggle button {
    border: 0;
    background: none;
    color: var(--ink-3);
    font: inherit;
    font-size: var(--t-xs);
    line-height: 1;
    padding: 6px 10px;
    border-radius: var(--r-pill);
    cursor: pointer;
    transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}

.theme-toggle button[aria-pressed="true"] {
    background: var(--surface);
    color: var(--ink);
    box-shadow: var(--lift-1);
}

/* ------------------------------------------------------------------ hero */

.tool-hero { padding: var(--sp-8) 0 var(--sp-6); text-align: center; }

/* Tight, large, optically balanced. The display face earns its keep here or
   nowhere. */
.tool-hero h1 {
    font-size: var(--t-3xl);
    letter-spacing: -.035em;
    line-height: .98;
}

.tool-hero p {
    margin: var(--sp-4) auto 0;
    max-width: 56ch;
    color: var(--ink-2);
    font-size: var(--t-lg);
    line-height: 1.5;
}

/* Small capsule above the H1 carrying the live colour. Gives the hero a focal
   point and tells you at a glance which page you are on. */
.eyebrow {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    padding: 6px 14px 6px 8px;
    border-radius: var(--r-pill);
    background: var(--surface);
    border: 1px solid var(--line);
    box-shadow: var(--lift-1), var(--sheen);
    font-size: var(--t-xs);
    font-weight: 600;
    letter-spacing: .04em;
    color: var(--ink-2);
    margin-bottom: var(--sp-4);
}

.eyebrow i {
    width: 16px; height: 16px;
    border-radius: var(--r-pill);
    background: var(--active);
    box-shadow: 0 0 0 1px rgba(0,0,0,.12), 0 0 12px -2px var(--active);
    flex: none;
}

/* ----------------------------------------------------------------- stage */
/* The tool itself. Deliberately dark even in the light theme. */

.stage {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 9;
    background: var(--stage);
    border-radius: 22px;
    /* Layered depth plus a glow in the page's own colour. */
    box-shadow: var(--lift-4), var(--glow);
    overflow: hidden;
    cursor: pointer;
    transition: transform var(--med) var(--ease), box-shadow var(--med) var(--ease);
}

.stage:hover { transform: translateY(-3px); }

/* Inner hairline drawn on top of the fill, so a white sample still has an edge
   and the panel keeps its form regardless of the colour inside it. */
.stage::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    box-shadow: inset 0 0 0 1px rgba(255,255,255,.10),
                inset 0 1px 0 rgba(255,255,255,.16);
}

/* The colour sample. Separate element so the stage keeps its chrome. */
.stage__fill {
    position: absolute;
    inset: 0;
    background: var(--active);
    transition: background var(--med) var(--ease);
}

.stage__hint {
    position: absolute;
    left: 50%;
    bottom: var(--sp-4);
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-4);
    border-radius: var(--r-pill);
    background: rgba(0, 0, 0, .55);
    color: #fff;
    font-size: var(--t-sm);
    backdrop-filter: blur(6px);
    pointer-events: none;
    opacity: 0;
    transition: opacity var(--med) var(--ease);
}

.stage:hover .stage__hint,
.stage:focus-visible .stage__hint { opacity: 1; }

.stage__expand {
    position: absolute;
    right: var(--sp-4);
    bottom: var(--sp-4);
    width: 34px; height: 34px;
    display: grid; place-items: center;
    border-radius: var(--r-sm);
    background: rgba(0, 0, 0, .45);
    color: #fff;
    pointer-events: none;
}

/* A light sample (white, beige) needs a visible edge against a light page. */
.stage[data-light="1"] .stage__fill { box-shadow: inset 0 0 0 1px var(--line-2); }

/* --------------------------------------------------------------- actions */

.action-row {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: var(--sp-3);
    margin-top: var(--sp-5);
}

.btn {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    font: inherit;
    font-size: var(--t-sm);
    font-weight: 600;
    padding: 11px 20px;
    border-radius: var(--r-sm);
    border: 1px solid var(--line-2);
    background: var(--surface);
    color: var(--ink);
    text-decoration: none;
    cursor: pointer;
    transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease);
}

.btn:hover { border-color: var(--ink-3); }

.btn {
    box-shadow: var(--lift-1), var(--sheen);
}

.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); }

.btn--primary {
    background: var(--ink);
    border-color: var(--ink);
    color: var(--ground);
    box-shadow: 0 1px 2px rgba(23,24,26,.18),
                0 8px 20px -10px color-mix(in srgb, var(--active) 55%, transparent),
                inset 0 1px 0 rgba(255,255,255,.14);
}

.btn--primary:hover {
    box-shadow: 0 2px 4px rgba(23,24,26,.2),
                0 14px 30px -12px color-mix(in srgb, var(--active) 70%, transparent),
                inset 0 1px 0 rgba(255,255,255,.14);
}

.kbd {
    font-family: var(--font-mono);
    font-size: var(--t-xs);
    padding: 3px 7px;
    border: 1px solid var(--line-2);
    border-bottom-width: 2px;
    border-radius: var(--r-sm);
    background: var(--sunken);
    color: var(--ink-2);
}

.hint { font-size: var(--t-sm); color: var(--ink-3); }

/* ----------------------------------------------------------- colour tiles */
/* Navigation and internal linking are the same component on purpose:
   every tile is a real <a href>, so the set is crawlable and usable. */

.tiles {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
    gap: var(--sp-3);
}

/* The inline grid on tool and colour pages: 19 swatches plus the "+28 more"
   tile. A wider track lands that on 5 columns at full width, so the 20 tiles
   fill exactly four rows with no ragged tail, and the swatches get big enough
   to be comfortable tap targets. /colors keeps the denser default, because it
   shows all 47 in groups and larger tiles there would only add scrolling. */
.tiles--major { grid-template-columns: repeat(auto-fill, minmax(128px, 1fr)); }

.tile {
    display: block;
    text-decoration: none;
    border-radius: 14px;
    padding: var(--sp-2);
    border: 1px solid transparent;
    transition: transform var(--fast) var(--ease), background var(--fast) var(--ease),
                border-color var(--fast) var(--ease);
}

.tile:hover { background: var(--surface); border-color: var(--line); transform: translateY(-3px); }
.tile:hover .tile__chip { box-shadow: var(--lift-2); }
.tile:active { transform: translateY(-1px); }

/* The swatch is the product, so it gets the crafted treatment: rounded, lifted,
   with an inner hairline so pale colours still read as an object. */
.tile__chip {
    display: block;
    width: 100%;
    aspect-ratio: 16 / 10;
    border-radius: 10px;
    box-shadow: var(--lift-1),
                inset 0 0 0 1px rgba(0,0,0,.10),
                inset 0 1px 0 rgba(255,255,255,.22);
    transition: box-shadow var(--fast) var(--ease);
}

.tile__label {
    display: block;
    margin-top: var(--sp-2);
    font-size: var(--t-sm);
    color: var(--ink-2);
    text-align: center;
}

/* The "+N more" tile. Same footprint as a swatch so the grid stays even, but
   drawn as an outline rather than a fill: it is a door, not a colour, and a
   solid chip here would read as a shade you could pick. */
.tile--more .tile__chip--more {
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--sunken);
    box-shadow: none;
    border: 1px dashed var(--line-2);
    color: var(--ink-3);
    font-size: var(--t-sm);
    font-weight: 600;
    letter-spacing: .02em;
    transition: border-color var(--fast) var(--ease), color var(--fast) var(--ease);
}
.tile--more:hover .tile__chip--more { border-color: var(--ink-3); color: var(--ink-2); }
.tile--more .tile__label { font-weight: 600; color: var(--ink-2); }

.tile[aria-current="page"] {
    border-color: var(--active);
    background: var(--sunken);
}

.tile[aria-current="page"] .tile__label { color: var(--ink); font-weight: 600; }

/* ---------------------------------------------------------------- panels */

.panel {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 16px;
    padding: var(--sp-5);
    box-shadow: var(--lift-2), var(--sheen);
}

.panel__title {
    font-size: var(--t-xs);
    text-transform: uppercase;
    letter-spacing: .12em;
    color: var(--ink-3);
    font-weight: 700;
    margin-bottom: var(--sp-4);
    font-family: var(--font-ui);
}

.field { display: flex; flex-direction: column; gap: var(--sp-2); }
.field label { font-size: var(--t-sm); color: var(--ink-2); }

.field input,
.field select {
    font: inherit;
    font-size: var(--t-sm);
    padding: 9px 12px;
    border-radius: var(--r-sm);
    border: 1px solid var(--line-2);
    background: var(--sunken);
    color: var(--ink);
    width: 100%;
}

.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); }

/* ---------------------------------------------------------------- layout */

.tool-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 300px;
    gap: var(--sp-6);
    align-items: start;
}

.section { margin-top: var(--sp-9); }

/* Section heads get a short rule in the active colour: a small structural
   device that gives long pages rhythm without adding decoration. */
.section > h2 {
    font-size: var(--t-2xl);
    letter-spacing: -.028em;
    margin-bottom: var(--sp-5);
    padding-top: var(--sp-3);
    position: relative;
}

.section > h2::before {
    content: "";
    position: absolute;
    top: 0; left: 0;
    width: 34px; height: 3px;
    border-radius: var(--r-pill);
    background: var(--active);
}

/* ----------------------------------------------------------- tool cards */

.cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(268px, 1fr));
    gap: var(--sp-4);
}

.card {
    position: relative;
    display: block;
    text-decoration: none;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 16px;
    padding: var(--sp-5);
    box-shadow: var(--lift-1), var(--sheen);
    overflow: hidden;
    transition: transform var(--med) var(--ease), box-shadow var(--med) var(--ease),
                border-color var(--med) var(--ease);
}

/* A wash that only appears on hover, in the page's colour. Cheap, and it makes
   the grid feel responsive rather than static. */
.card::before {
    content: "";
    position: absolute;
    inset: 0;
    opacity: 0;
    pointer-events: none;
    background: radial-gradient(80% 60% at 50% 0%,
        color-mix(in srgb, var(--active) 12%, transparent) 0%, transparent 70%);
    transition: opacity var(--med) var(--ease);
}

.card:hover {
    transform: translateY(-4px);
    box-shadow: var(--lift-3), var(--sheen);
    border-color: color-mix(in srgb, var(--active) 34%, var(--line));
}

.card:hover::before { opacity: 1; }

.card h3 {
    font-size: var(--t-lg);
    margin-bottom: var(--sp-2);
    letter-spacing: -.02em;
    position: relative;
}

.card p { margin: 0; font-size: var(--t-sm); color: var(--ink-2); position: relative; }

/* -------------------------------------------------------------- prose */

/* Wallpaper block */
.wp { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-6); align-items: start; }
.wp__img {
    display: block;
    border-radius: var(--r-md);
    overflow: hidden;
    box-shadow: var(--lift-2);
    line-height: 0;
}
.wp__img img { width: 100%; height: auto; display: block; }
.panel__note {
    margin: calc(var(--sp-3) * -1) 0 var(--sp-4);
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--ink-3);
}
@media (max-width: 760px) { .wp { grid-template-columns: 1fr; gap: var(--sp-4); } }

/* Colour index search */
.colorsearch { max-width: 520px; margin: 0 auto var(--sp-5); text-align: center; }
.colorsearch input {
    width: 100%;
    font: inherit;
    font-size: var(--t-md);
    padding: 13px 18px;
    border-radius: var(--r-pill);
    border: 1px solid var(--line-2);
    background: var(--surface);
    color: var(--ink);
    box-shadow: var(--lift-1), var(--sheen);
}
.colorsearch input:focus { outline: 2px solid var(--active); outline-offset: 2px; }
.colorsearch .hint { margin-top: var(--sp-2); }

.tile__hex {
    display: block;
    text-align: center;
    font-size: 10.5px;
    color: var(--ink-3);
    margin-top: 1px;
}

.empty-msg {
    text-align: center;
    color: var(--ink-2);
    padding: var(--sp-7) 0;
}

/* Colour values table */
.scroller { overflow-x: auto; }
.vals {
    border-collapse: collapse;
    font-size: var(--t-sm);
    min-width: 300px;
    max-width: 460px;
    width: 100%;
}
.vals th, .vals td {
    text-align: left;
    padding: 10px 14px;
    border-bottom: 1px solid var(--line);
}
.vals tr:last-child th, .vals tr:last-child td { border-bottom: 0; }
.vals th { color: var(--ink-2); font-weight: 500; width: 45%; }
.vals td { color: var(--ink); font-variant-numeric: tabular-nums; }
.vals tr:nth-child(odd) { background: var(--sunken); }

.prose h3 {
    font-size: var(--t-lg);
    margin: var(--sp-5) 0 var(--sp-2);
    letter-spacing: -.02em;
}

.prose { max-width: 68ch; }
.prose p { color: var(--ink-2); margin: 0 0 var(--sp-4); }
.prose h2 { margin-bottom: var(--sp-3); }

/* The direct-answer paragraph. Set slightly larger and in the primary ink so it
   reads as the answer to the page's own title, which is what both a skimming
   reader and an answer engine are looking for first. */
.prose .lede-para {
    font-size: var(--t-md);
    color: var(--ink);
    line-height: 1.65;
}

.steps {
    margin: 0 0 var(--sp-4);
    padding: 0;
    list-style: none;
    counter-reset: step;
}
.steps li {
    position: relative;
    counter-increment: step;
    padding: 0 0 var(--sp-3) var(--sp-6);
    color: var(--ink-2);
}
.steps li::before {
    content: counter(step);
    position: absolute;
    left: 0;
    top: 1px;
    width: 22px; height: 22px;
    border-radius: var(--r-pill);
    background: var(--sunken);
    border: 1px solid var(--line);
    color: var(--ink-2);
    font-size: var(--t-xs);
    font-weight: 700;
    display: flex;
    align-items: center;
    justify-content: center;
}
.steps li:last-child { padding-bottom: 0; }

.faq { border-top: 1px solid var(--line); }
.faq details { border-bottom: 1px solid var(--line); padding: var(--sp-4) 0; }
.faq summary { cursor: pointer; font-weight: 600; list-style: none; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; float: right; color: var(--ink-3); }
.faq details[open] summary::after { content: "\2212"; }
.faq p { margin: var(--sp-3) 0 0; color: var(--ink-2); }

/* --------------------------------------------------------------- footer */

.site-foot {
    margin-top: var(--sp-9);
    border-top: 1px solid var(--line);
    padding: var(--sp-6) 0;
    color: var(--ink-3);
    font-size: var(--t-sm);
}

.site-foot__in {
    max-width: var(--maxw);
    margin: 0 auto;
    padding: 0 var(--sp-5);
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-4) var(--sp-5);
    justify-content: space-between;
}

.site-foot a { color: var(--ink-2); text-decoration: none; }
.site-foot a:hover { color: var(--ink); }

/* ---------------------------------------------------------------- rating */

.rating {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2) var(--sp-4);
    margin-top: var(--sp-5);
    padding: var(--sp-4);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--surface);
}

.rating__ask {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
}

.rating__q {
    font-size: var(--t-sm);
    color: var(--ink-2);
}

.rating__stars { display: flex; gap: 2px; }

.rating__star {
    display: block;
    padding: 2px;
    border: 0;
    background: none;
    cursor: pointer;
    line-height: 0;
    border-radius: var(--r-sm);
}

.rating__star svg {
    width: 22px;
    height: 22px;
    /* Empty stars are outlines, not grey fills. A row of solid grey stars reads
       as a score of zero rather than as an invitation. */
    fill: none;
    stroke: var(--line-2);
    stroke-width: 1.4;
    transition: fill .12s ease, stroke .12s ease;
}

.rating__star.is-on svg { fill: var(--active); stroke: var(--active); }

.rating__star:focus-visible {
    outline: 2px solid var(--active);
    outline-offset: 2px;
}

.rating.is-done .rating__star { cursor: default; }

.rating__score {
    margin: 0;
    font-size: var(--t-sm);
    color: var(--ink-2);
}

.rating__score strong { color: var(--ink); }

.rating__thanks {
    margin: 0;
    font-size: var(--t-sm);
    color: var(--ok);
}

/* Once voted, the prompt is dead weight; the score and the thanks carry it. */
.rating.is-done .rating__q { display: none; }

/* ------------------------------------------------------------ responsive */

@media (max-width: 900px) {
    .tool-layout { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 640px) {
    .wrap, .site-head__in, .site-foot__in { padding-left: var(--sp-4); padding-right: var(--sp-4); }
    .tool-hero { padding: var(--sp-5) 0 var(--sp-4); }
    .tiles { grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); gap: var(--sp-2); }

    /* Fixed four across rather than auto-fill: 20 tiles then land on exactly
       five rows with nothing ragged, instead of the seven a 3-column fallback
       produced. Still ~80px per swatch at 360px, which is a fine tap target. */
    .tiles--major { grid-template-columns: repeat(4, 1fr); }
    .site-nav { gap: var(--sp-3); }
    .site-nav a { font-size: var(--t-xs); }
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { transition: none !important; animation: none !important; }
}

/* ------------------------------------------------- test tools (batch 1) */

/* The photosensitivity gate on /pixel-fixer. Styled as a warning rather than a
   notice on purpose: it is the one thing on this site that can actually harm
   someone, so it should not look like a cookie banner. */
.warn {
    border: 1px solid var(--warn);
    border-left-width: 4px;
    border-radius: var(--r-md);
    background: color-mix(in srgb, var(--warn) 7%, var(--surface));
    padding: var(--sp-5);
}
.warn__h { margin: 0 0 var(--sp-3); font-size: var(--t-lg); }
.warn p  { color: var(--ink-2); margin: 0 0 var(--sp-3); }
.warn .btn { margin-top: var(--sp-2); }

/* The draggable patch. Absolute inside .stage, which is position:relative. */
.fixer-patch {
    position: absolute;
    left: 40%; top: 40%;
    width: 96px; height: 96px;
    border-radius: 8px;
    background: #FF0000;
    box-shadow: 0 0 0 2px rgba(255,255,255,.85), 0 0 0 4px rgba(0,0,0,.55);
    cursor: grab;
    touch-action: none;   /* or a drag scrolls the page on touch instead */
    z-index: 3;
}
.fixer-patch:active { cursor: grabbing; }

/* What to look for on the current step. This is the part that makes a test
   tool useful rather than just a coloured rectangle. */
.testcard {
    margin-top: var(--sp-4);
    padding: var(--sp-4);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--surface);
}
.testcard__step {
    margin: 0 0 var(--sp-2);
    font-size: var(--t-xs);
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--ink-3);
}
.testcard__look { margin: 0; color: var(--ink-2); }

.testnav { list-style: none; margin: 0; padding: 0; }
.testnav li + li { margin-top: 2px; }
.testnav button {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    width: 100%;
    padding: 8px 10px;
    margin: 0 -10px;
    border: 0;
    border-radius: var(--r-sm);
    background: none;
    font: inherit;
    font-size: var(--t-sm);
    color: var(--ink-2);
    text-align: left;
    cursor: pointer;
}
.testnav button:hover { background: var(--sunken); color: var(--ink); }
.testnav button[aria-current="step"] { background: var(--sunken); color: var(--ink); font-weight: 600; }
.testnav__chip {
    width: 22px; height: 16px;
    border-radius: 4px;
    flex: none;
    box-shadow: inset 0 0 0 1px rgba(0,0,0,.18);
}

/* Nine-zone grid. Uniformity is measured by comparing regions, because the eye
   misses a slow gradient across a whole panel. */
.grid-overlay {
    position: absolute;
    inset: 0;
    z-index: 2;
    pointer-events: none;
    background-image:
        linear-gradient(to right,  rgba(255,0,0,.55) 1px, transparent 1px),
        linear-gradient(to bottom, rgba(255,0,0,.55) 1px, transparent 1px);
    background-size: 33.333% 33.333%;
    background-position: 33.333% 33.333%;
}

/* [hidden] is only a UA rule of display:none, so ANY class rule that sets
   display beats it. .action-row is display:flex, which meant the pixel fixer's
   Start button stayed on screen while its photosensitivity warning was still
   unacknowledged: the one place on this site where a hidden element failing to
   hide actually matters. Global, because the same trap applies to every
   flex/grid component here. */
[hidden] { display: none !important; }

/* ---------------------------------------------- ambient tools (batch 2) */

/* Canvas tools own their whole box; the stage is only a frame around them. */
.stage--canvas { background: #000; overflow: hidden; }
.stage--canvas canvas { display: block; width: 100%; height: 100%; }

/* Corner hit. The flash IS the payoff, so it should be unmistakable. */
.stage.is-corner { box-shadow: 0 0 0 3px var(--active), 0 0 60px -10px var(--active); }
.stage.is-corner::after {
    content: "CORNER";
    position: absolute; top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    font-family: var(--font-display);
    font-size: clamp(24px, 6vw, 64px);
    letter-spacing: .1em;
    color: #fff;
    text-shadow: 0 0 24px rgba(0,0,0,.9);
    animation: cornerpop .9s var(--ease) both;
    pointer-events: none;
}
@keyframes cornerpop {
    0%   { opacity: 0; transform: translate(-50%,-50%) scale(.7); }
    18%  { opacity: 1; transform: translate(-50%,-50%) scale(1); }
    70%  { opacity: 1; }
    100% { opacity: 0; }
}

/* Checkbox rows in a panel. display:flex on a <label> collapses its own
   margin against the next element, which let the settings note overlap the
   labels above it on the flip clock. An explicit block margin fixes it. */
.check {
    display: flex; align-items: center; gap: var(--sp-2);
    font-size: var(--t-sm); color: var(--ink-2); cursor: pointer;
    margin-bottom: var(--sp-3);
}
.check:last-child { margin-bottom: 0; }
.check input { flex: none; margin: 0; }

/* ------------------------------------------------------------ flip clock */

.stage--clock {
    background: #101113;
    display: flex; align-items: center; justify-content: center;
    padding: clamp(8px, 2vw, 28px);
}
.stage--clock.is-dim { filter: brightness(.42) contrast(.9); }

/* Sized off the STAGE, not the viewport. cqw is a fraction of this container's
   width, so six digits plus separators always fit whatever box they are in:
   the small preview box, and the full screen. Sizing off vw overflowed the
   preview badly, since the stage is only part of the page width. */
.flip {
    container-type: inline-size;
    display: flex; align-items: center; justify-content: center;
    gap: 1.2cqw;
    width: 100%;
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
}
.flip__group { display: flex; gap: .7cqw; }
.flip__sep {
    color: #E8E9EB; font-size: 9cqw; font-weight: 700;
    opacity: .7; line-height: 1; padding: 0 .4cqw;
}
.flip__ampm {
    color: #8B8F96; font-family: var(--font-ui); font-weight: 700;
    font-size: 2.4cqw; letter-spacing: .12em;
    align-self: flex-start; margin-top: 1cqw;
}

/* One digit: two static halves, with two leaves that animate over them. This
   is the split-flap mechanism rather than a cross-fade, which is the point. */
.flipd {
    position: relative;
    width: 11cqw;
    height: 16.5cqw;
    border-radius: 1cqw;
    background: #16181B;
    box-shadow: inset 0 0 0 1px rgba(255,255,255,.05);
    perspective: 40cqw;
}
.flipd__top, .flipd__bot, .flipd__fold {
    position: absolute; left: 0; right: 0; height: 50%;
    overflow: hidden;
    background: #24262B;
    backface-visibility: hidden;
}
.flipd__top, .flipd__fold--top {
    top: 0;
    border-radius: 1cqw 1cqw 0 0;
    border-bottom: 1px solid rgba(0,0,0,.6);
}
.flipd__bot, .flipd__fold--bot {
    bottom: 0;
    border-radius: 0 0 1cqw 1cqw;
    background: #1E2024;
}

/* The glyph is drawn at FULL digit height inside each half and pushed so that
   the top half shows its upper portion and the bottom half its lower one.
   Line-height and offset are in the same unit so the seam lands exactly on the
   middle of the number rather than near it. */
.flipd__top::after, .flipd__bot::after,
.flipd__fold--top::after, .flipd__fold--bot::after {
    content: attr(data-d);
    position: absolute; left: 0; right: 0;
    height: 16.5cqw; line-height: 16.5cqw;
    text-align: center;
    font-size: 12cqw; font-weight: 700;
    color: #F4F5F6;
}
.flipd__top::after, .flipd__fold--top::after { top: 0; }
.flipd__bot::after, .flipd__fold--bot::after { bottom: 0; }

.flipd__fold { display: none; z-index: 2; }
.flipd.is-flipping .flipd__fold { display: block; }
.flipd.is-flipping .flipd__fold--top {
    transform-origin: bottom; animation: foldtop .5s ease-in both;
}
.flipd.is-flipping .flipd__fold--bot {
    transform-origin: top; animation: foldbot .5s ease-out both;
}
@keyframes foldtop { from { transform: rotateX(0deg); } to { transform: rotateX(-90deg); } }
@keyframes foldbot { from { transform: rotateX(90deg); } to { transform: rotateX(0deg); } }

@media (prefers-reduced-motion: reduce) {
    .flipd.is-flipping .flipd__fold { animation: none; display: none; }
    .stage.is-corner::after { animation: none; opacity: 1; }
}

/* ------------------------------------------- practical tools (batch 3) */

.stage--light .stage__fill { transition: background var(--fast) linear; }

/* Info table. Grid rather than a <table> because each cell carries a caption
   under it, which a table row cannot express without nesting. */
.infogrid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
    gap: var(--sp-3);
}
.infogrid__item {
    padding: var(--sp-4);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--surface);
}
.infogrid__k {
    display: block; font-size: var(--t-xs); letter-spacing: .08em;
    text-transform: uppercase; color: var(--ink-3);
}
.infogrid__v {
    display: block; margin-top: 4px; font-size: var(--t-lg);
    font-weight: 600; color: var(--ink);
    overflow-wrap: anywhere;      /* GPU strings are long and unbreakable */
}
.infogrid__n { display: block; margin-top: 2px; font-size: 12px; color: var(--ink-3); }

.bignum {
    margin: 0; font-size: clamp(26px, 4vw, 40px);
    font-weight: 700; color: var(--ink); letter-spacing: -.02em;
}

/* Touch test: the surface must own its gestures, or dragging scrolls the page
   instead of painting, which makes the tool untestable on the devices it is
   actually for. */
.stage--touch { background: #101113; touch-action: none; overscroll-behavior: contain; cursor: crosshair; }
.stage--touch canvas { display: block; width: 100%; height: 100%; }

.stage--rate {
    background: #101113;
    display: flex; flex-direction: column;
    align-items: center; justify-content: center; gap: var(--sp-4);
}
.rate { text-align: center; }
.rate__num { margin: 0; font-size: clamp(48px, 11vw, 128px); font-weight: 700; color: #F4F5F6; line-height: 1; }
.rate__unit { margin: var(--sp-2) 0 0; font-size: var(--t-sm); color: #8B8F96; letter-spacing: .14em; text-transform: uppercase; }
.rate__sub { margin: var(--sp-3) 0 0; font-size: var(--t-sm); color: #C9CDD3; }
.stage--rate canvas { width: 100%; height: 64px; }

input[type="range"] { width: 100%; accent-color: var(--active); }
input[type="file"] { font-size: var(--t-sm); color: var(--ink-2); }
