/*
  style.css - layout and components for the Machinon landing page.

  Colours AND shadows come from tokens.css, generated from the theme's own
  schemes, so nothing in this file declares a colour or shadow literal at all.
  Elevation is consumed as var(--dz-elev-card|popup|overlay) rather than copied:
  dark.css deepens every alpha (card 0.25 -> 0.50) so shadows read against a
  dark underlay, and hardcoding the light values here would paint all four dark
  schemes too weakly (DESIGN.md > Elevation > Dark Underlay).

  The only remaining --site-* variables are geometry and type, which carry no
  per-scheme value: radius, the type scale, and the max width.

  Type: body and UI text use the theme's scale (11/12/14/16/20/26). The three
  --site-text-display-* sizes are the documented site-only display tier; they
  are not --dz-* tokens and must never be used by theme CSS (DESIGN.md >
  Typography > Site-only display tier).
*/

@font-face {
    font-family: 'Inter';
    src: url('fonts/Inter-Regular.woff2') format('woff2');
    font-weight: 400;
    font-display: swap;
}
@font-face {
    font-family: 'Inter';
    src: url('fonts/Inter-SemiBold.woff2') format('woff2');
    font-weight: 600;
    font-display: swap;
}
@font-face {
    font-family: 'JetBrains Mono';
    src: url('fonts/JetBrainsMono-Regular.woff2') format('woff2');
    font-weight: 400;
    font-display: swap;
}

:root {
    --site-font: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
    --site-font-mono: 'JetBrains Mono', ui-monospace, Consolas, Menlo, monospace;

    /* Theme type scale (DESIGN.md) */
    --site-text-micro: 0.6875rem;
    --site-text-xs: 0.75rem;
    --site-text-sm: 0.875rem;
    --site-text-md: 1rem;
    --site-text-lg: 1.25rem;
    --site-text-xl: 1.625rem;

    /* Site-only display tier: 32 / 40 / 56px. NOT theme tokens. */
    --site-text-display-sm: 2rem;
    --site-text-display-md: 2.5rem;
    --site-text-display-lg: 3.5rem;

    /* DESIGN.md > Border Radius */
    --site-radius-interactive: 5px;
    --site-radius-container: 6px;
    --site-radius-button: 10px;

    --site-max-width: 1120px;
}

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

body {
    margin: 0;
    background: var(--dz-body-bg);
    color: var(--dz-body-text);
    font-family: var(--site-font);
    font-size: var(--site-text-sm);
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; height: auto; display: block; }
/* `img { display: block }` above out-specifies the UA sheet's
   `[hidden] { display: none }`, so a hidden <img> would still paint. The
   garage card keeps both state PNGs in the DOM and hides one, so this rule
   is what makes that work at all. */
img[hidden] { display: none; }
code, pre { font-family: var(--site-font-mono); }

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

/* ---- Base ------------------------------------------------------------ */

/* Not a colour, a keyword: it tells the browser which way to paint the
   native controls it owns (the picker's dropdown list, scrollbars) so they
   do not stay light inside a dark scheme.

   Root-scoped on purpose. Every .scheme-swatch carries its own data-scheme, so
   an unscoped [data-scheme="*-dark"] would also match the four dark swatch
   buttons and flip their native rendering while the page itself is light. */
:root { color-scheme: light; }
:root[data-scheme="machinon-dark"],
:root[data-scheme="magenta-dark"],
:root[data-scheme="paper-dark"],
:root[data-scheme="gruvbox-dark"] { color-scheme: dark; }

figure { margin: 0; }

h1, h2, h3 {
    font-weight: 600;
    line-height: 1.2;
}

/* Inline code in prose. The tint is the accent at low alpha, so it stays a
   tint of whatever the active scheme's accent is, including the monochrome
   Paper schemes where it reads as a plain grey chip. */
code {
    font-size: var(--site-text-xs);
    padding: 0.15em 0.4em;
    border-radius: 3px;
    background: rgba(var(--dz-accent-values), 0.10);
}

a:focus-visible,
button:focus-visible,
select:focus-visible {
    outline: 2px solid var(--dz-accent-color);
    outline-offset: 2px;
}

/* One container geometry, shared by every block that sits directly in a
   section. .device-grid below declares the same numbers itself. */
.hero,
.section-head,
.feature-grid,
.scheme-grid,
.showcase-shot,
.phone-grid,
.install-steps,
.install-blocks,
.footer-inner,
.footer-credits {
    max-width: var(--site-max-width);
    margin-left: auto;
    margin-right: auto;
    padding-left: 1.25rem;
    padding-right: 1.25rem;
}

main > section {
    padding-top: 4.5rem;
    padding-bottom: 4.5rem;
}

/* Sticky nav clearance for the in-page anchors. */
main > section[id] { scroll-margin-top: 4.5rem; }

/* Quiet banding: alternate sections sit on the nav surface instead of the
   body surface, which is a real tonal step in all eight schemes without
   introducing a colour that is not already in the palette. */
.cards-demo,
.schemes,
.mobile { background: var(--dz-nav-bg); }

/* ---- Nav ------------------------------------------------------------- */

.nav {
    position: sticky;
    top: 0;
    z-index: 100;
    background: var(--dz-nav-bg);
    border-bottom: 1px solid var(--dz-input-border);
}

.nav-inner {
    max-width: var(--site-max-width);
    margin: 0 auto;
    padding: 0.6rem 1.25rem;
    display: flex;
    align-items: center;
    gap: 1.5rem;
}

.wordmark {
    font-size: var(--site-text-lg);
    font-weight: 600;
    color: var(--dz-body-text);
    text-decoration: none;
    margin-right: auto;
}

.nav-links { display: flex; gap: 0.25rem; }

.nav-links a {
    color: var(--secondary-text-color);
    font-size: var(--site-text-sm);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    padding: 0.35rem 0.65rem;
    border-radius: var(--site-radius-interactive);
}

.nav-links a:hover {
    color: var(--dz-accent-text);
    background: var(--dz-accent-color);
}

#scheme-picker {
    font-family: inherit;
    font-size: var(--site-text-sm);
    color: var(--dz-body-text);
    background: var(--dz-widget-bg);
    border: 1px solid var(--dz-input-border);
    border-radius: var(--site-radius-interactive);
    padding: 0.35rem 0.5rem;
}

.scheme-control { display: flex; align-items: center; flex: 0 0 auto; }

/* ---- Buttons --------------------------------------------------------- */

.btn {
    display: inline-block;
    padding: 0.7rem 1.5rem;
    border-radius: var(--site-radius-button);
    font-size: var(--site-text-xs);
    font-weight: 600;
    text-decoration: none;
    box-shadow: var(--dz-elev-card);
}

.btn-primary { background: var(--dz-accent-color); color: var(--dz-accent-text); }

/* The accent carries the affordance (border), the text does not. Accent-as-text
   on --dz-widget-bg is only 4.31:1 in Gruvbox Dark, under the 4.5 floor for
   small text, and these buttons sit on a card inside .install-card. Body text
   on the border keeps the Paper monochrome behaviour intact and clears AA in
   all eight schemes. */
.btn-outline {
    background: transparent;
    color: var(--dz-body-text);
    border: 2px solid var(--dz-accent-color);
    box-shadow: none;
}

.btn-sm { padding: 0.45rem 1rem; }

/* Hover swaps to the filled pair rather than tinting, so it stays legible in
   the monochrome Paper schemes where the accent has no hue to lighten. */
.btn-outline:hover {
    background: var(--dz-accent-color);
    color: var(--dz-accent-text);
}

/* ---- Hero ------------------------------------------------------------ */

.hero {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
    align-items: center;
    gap: 3rem;
    padding-top: 5rem;
    padding-bottom: 5rem;
}

.hero h1 {
    margin: 0;
    font-size: var(--site-text-display-lg);
    letter-spacing: -0.02em;
}

.hero-sub {
    margin: 1.25rem 0 0;
    max-width: 46ch;
    font-size: var(--site-text-md);
    color: var(--secondary-text-color);
}

.hero-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: 2rem;
}

.hero-hint {
    margin: 1.75rem 0 0;
    padding-left: 0.85rem;
    max-width: 44ch;
    border-left: 3px solid var(--dz-accent-color);
    font-size: var(--site-text-xs);
    color: var(--secondary-text-color);
}

.hero-shot img {
    border: 1px solid var(--dz-input-border);
    border-radius: var(--site-radius-container);
    box-shadow: var(--dz-elev-overlay);
}

/* ---- Section heads --------------------------------------------------- */

.section-head {
    text-align: center;
    margin-bottom: 2.5rem;
}

.section-label {
    display: block;
    font-size: var(--site-text-micro);
    font-weight: 600;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--dz-accent-color);
}

.section-head h2 {
    margin: 0.5rem 0 0;
    font-size: var(--site-text-display-sm);
    letter-spacing: -0.01em;
}

.section-desc {
    margin: 0.9rem auto 0;
    max-width: 62ch;
    font-size: var(--site-text-md);
    color: var(--secondary-text-color);
}

/* ---- Device cards: the live demo ------------------------------------- */

/* This is the theme's REAL card, not an approximation. The grid below is a
   copy of css/cards.css:255-272: same 3 rows, same 6 columns, same areas, same
   10px gap, so the demo reads as Machinon rather than as a generic card.

   The column and row constants mirror --dz-card-* in dz-tokens.css. They are
   NOT in site/tokens.css and must not be added there: that file is generated
   from the schemes and carries colour and elevation only, both of which vary
   per scheme. Card geometry is scheme-independent, so it belongs here beside
   --site-radius-*, the same way the type scale does. */
.device-grid {
    --site-card-col-icon: 48px;        /* --dz-card-col-icon */
    --site-card-col-icon2: 18px;       /* --dz-card-col-icon2 */
    --site-card-col-trailing: 15px;    /* --dz-card-col-trailing */
    --site-card-row-status-min: 58px;  /* --dz-card-row-status-min */
    --site-card-row-lastupdate-h: 20px;/* --dz-card-row-lastupdate-h */
    --site-card-gap: 10px;             /* literal in cards.css too, see its note */
    --site-card-space-lg: 10px;        /* --dz-card-space-lg */
    --site-card-space-sm: 5px;         /* --dz-card-space-sm */
    --site-card-space-2xl: 20px;       /* --dz-card-space-2xl */
    --site-card-img-size: 40px;        /* --dz-card-img-size, inside the 48px column */
    --site-card-border-width: 1.5px;   /* --dz-card-border-width */
    --site-card-name-line: 18px;       /* --dz-card-name-line-height */
    --site-card-value-line: 24px;      /* --dz-card-value-line-height */
    --site-card-favorite-line: 16px;   /* --dz-card-favorite-line-height */

    /* theme.json ships card_min_width 320 and card_max_width 500, so the demo
       sizes itself into that band instead of stretching to the full container.
       Narrower than --site-max-width on purpose: at 1120px four cards would be
       249px each, below the theme's own minimum, and three columns would leave
       one orphan. Two columns land at ~470px, a realistic Machinon card, and
       the four shapes read as a 2x2 matrix. */
    max-width: 1000px;
    margin: 0 auto;
    padding: 0 1.25rem;
    display: grid;
    /* The track max stays 1fr and the 500px cap lives on the CARD, which is not
       interchangeable. css-grid-1 7.2.3.1 sizes an auto-repeat by the track's
       MAX function when that is definite, so minmax(320px, 500px) computes
       floor((960 + 20) / (500 + 20)) = ONE column and the strip collapses to a
       single card per row. With 1fr the max is indefinite, the count falls back
       to the 320px min, and 960px gives the intended two. min(320px, 100%)
       keeps that floor from forcing a horizontal scrollbar under a 320px
       viewport. Measured, not derived: see the round-3 report. */
    grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
    justify-items: center;
    gap: 1.25rem;
}

.device-card {
    /* theme.json's card_max_width. Capping the card rather than the track is
       what lets a lone card at a ~600px viewport stay 500px instead of
       stretching to 560px, without breaking the column count above. */
    width: 100%;
    max-width: 500px;
    display: grid;
    grid-template-rows: auto minmax(var(--site-card-row-status-min), 1fr)
                        minmax(0, var(--site-card-row-lastupdate-h));
    grid-template-columns: var(--site-card-col-icon) var(--site-card-col-icon2)
                           minmax(0, 1fr) minmax(0, auto) minmax(0, auto)
                           var(--site-card-col-trailing);
    grid-template-areas:
        'name name name bigtext bigtext options'
        'img  img2 status status status status'
        'favorite lastupdate lastupdate lastupdate tools tools';
    gap: var(--site-card-gap);
    background: var(--dz-widget-bg);
    color: var(--dz-widget-text);
    /* Transparent 1.5px border, as the theme does: it reserves the ring the
       device-status glows paint into, so the box geometry matches. */
    border: var(--site-card-border-width) solid transparent;
    border-radius: var(--site-radius-container);
    box-shadow: var(--dz-elev-card);
    font-size: var(--site-text-sm);
}

/* Row 1, columns 1-3. */
.dc-name {
    grid-area: name;
    padding: var(--site-card-space-lg) 0 0 var(--site-card-space-lg);
    line-height: var(--site-card-name-line);
    color: var(--dz-widget-text);
    min-width: 0;
    overflow-wrap: break-word;
}

/* Row 1, columns 4-5. The device value, ACCENT-coloured and hard right, beside
   the name rather than under it. This is the single most recognisable thing
   about a Machinon card (css/cards.css .item #bigtext). */
.dc-bigtext {
    grid-area: bigtext;
    justify-self: right;
    text-align: right;
    padding: var(--site-card-space-lg) 0 0 var(--site-card-space-lg);
    font-size: var(--site-text-lg);
    line-height: var(--site-card-value-line);
    color: var(--dz-accent-color);
    font-variant-numeric: tabular-nums;
    min-width: 0;
}

/* Row 1, column 6: the options (3-dot) affordance. */
.dc-options {
    grid-area: options;
    align-self: start;
    padding-top: var(--site-card-space-lg);
    color: var(--secondary-text-color);
    line-height: 0;
}

/* Row 2, column 1: the 48px device icon column, 40px image, bottom aligned.

   History: this cell once had NO padding at all, because the theme's own
   cards.css padding interacted badly with this page's global
   `img { max-width: 100% }` reset (the reset clamps width only, so a 40px
   icon painted 28x40, a squashed square). That squash is now impossible: the
   `.dc-img img` rule below pins both dimensions with `max-width: none`. What
   the no-padding version got wrong instead was the card's left gutter; the
   real theme's icon never touches the card edge (owner-reported 2026-08-16),
   so the cell now carries the same left inset as .dc-name above it. */
.dc-img {
    grid-area: img;
    align-self: end;
    padding-left: var(--site-card-space-lg);
    text-align: center;
    line-height: 0;
}

.dc-img img {
    width: var(--site-card-img-size);
    height: var(--site-card-img-size);
    /* Overrides the global `img { max-width: 100% }` reset. Belt and braces:
       with the padding gone nothing clamps today, but this is the declaration
       that guarantees a future container change cannot silently distort a
       fixed-size icon again. */
    max-width: none;
}

/* Row 2, columns 3-6: status text and any control (toggle, slider). */
.dc-status {
    grid-area: status;
    align-self: center;
    justify-self: right;
    padding-right: var(--site-card-space-2xl);
    max-width: 100%;
    color: var(--dz-body-text);
}

/* Row 3. The theme pins the star and the tool pair to the ends of the whole
   row and right-aligns the timestamp inside its own area. */
.dc-favorite {
    grid-row: 3;
    grid-column: 1 / -1;
    justify-self: start;
    margin-left: var(--site-card-space-lg);
    line-height: var(--site-card-favorite-line);
    color: var(--dz-accent-color);
}

.dc-lastupdate {
    grid-area: lastupdate;
    justify-self: right;
    align-self: center;
    font-size: var(--site-text-xs);
    color: var(--secondary-text-color);
    font-variant-numeric: tabular-nums;
}

/* Log and timer glyphs. dz-tokens.css defines --dz-card-icon-accent as the
   plain accent and documents that it clears WCAG 1.4.11's 3:1 non-text
   threshold on the card in both schemes, so the accent is used directly. */
.dc-tools {
    grid-row: 3;
    grid-column: 1 / -1;
    justify-self: end;
    display: flex;
    gap: var(--site-card-space-sm);
    padding-right: var(--site-card-space-sm);
    line-height: var(--site-card-favorite-line);
    color: var(--dz-accent-color);
}

.dc-options svg,
.dc-favorite svg,
.dc-tools svg { display: block; fill: currentColor; }

/* The garage card stacks both state icons in the 48px cell and hides one, so
   the first toggle never waits on a network fetch. Only one is ever visible, so
   no positioning is needed; `img[hidden]` above does the work. */

/* Status row content: the label and its control sit on one line, right aligned
   like the theme's own status cell. */
.dc-status {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--site-card-space-sm);
}

/* Toggle: a faithful copy of css/switch.css (theme lines 10-60), not an
   approximation of it.

   The earlier version inverted the theme's own logic. The theme carries the
   control's perceivability on the KNOB, which is a text-grade colour
   (--secondary-text-color when off, --dz-accent-color when on) with an
   --dz-elev-card drop shadow, and leaves the TRACK a faint accent tint
   (0.2 off, 0.5 on). The demo had a solid --dz-status-disabled track and a
   --dz-widget-bg knob, which is why it read as not-quite-Machinon and why the
   off track measured 1.78:1 against the card in Gruvbox Dark.

   The geometry is the theme's too, and it is not the conventional contained
   pill it replaced: a 40x15 track with a 20x20 knob that overhangs both ends
   (left: -10px at rest, translateX(34px) when on). --dz-btn-focus-ring is not
   one of the tokens site/tokens.css generates, so the focus ring stays this
   page's shared outline; that is the single deviation, and an outline is the
   more robust affordance anyway. */
.switch {
    position: relative;
    display: inline-block;
    width: 40px;
    height: 15px;
    /* The theme's own margin-right, plus a matching left margin so the knob's
       10px left overhang has somewhere to sit inside the flex status row
       instead of colliding with the label. The theme gets that room from
       .switch-cell's 30px left padding, which this page has no equivalent of. */
    margin-left: 10px;
    margin-right: 10px;
    border-radius: 34px;
    background-color: rgba(var(--dz-accent-values), 0.2);
    vertical-align: middle;
}

/* The garage toggle is a real <button>, so it needs the UA chrome removed
   while keeping the same switch geometry as the static ones. Focus visibility
   comes from the shared button:focus-visible ring near the top of this file. */
button.switch:disabled { cursor: default; }

button.switch {
    padding: 0;
    border: 0;
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
}

.switch.is-on { background-color: rgba(var(--dz-accent-values), 0.5); }

.switch-knob {
    position: absolute;
    left: -10px;
    bottom: -2px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background-color: var(--secondary-text-color);
    box-shadow: var(--dz-elev-card);
}

.switch.is-on .switch-knob {
    transform: translateX(34px);
    background-color: var(--dz-accent-color);
}

/* Dimmer slider, in the status row beside the value. Geometry from
   --dz-card-slider-* (5px track, 3px radius, 15px handle). The theme's track
   colour is --dz-card-slider-track-bg, a raw rgba(0,0,0,0.26) that
   dz-tokens.css itself flags as a leftover with no accent tie; the site has a
   no-colour-literals rule, so the track takes --dz-input-border instead. That
   is the one deliberate deviation from the real card. */
.slider {
    position: relative;
    display: inline-block;
    vertical-align: middle;
    width: 100px;
    height: 5px;
    border-radius: 3px;
    background: var(--dz-input-border);
}

/* The hallway dimmer's slider is live (site/app.js wireDimmerCard).
   touch-action: none keeps a drag on the track from scrolling the page on
   touch screens; pointer capture in the JS does the rest. */
#dimmer-slider {
    cursor: pointer;
    touch-action: none;
}
#dimmer-slider[aria-disabled="true"] {
    cursor: default;
}

.slider-fill {
    position: absolute;
    inset: 0 38% 0 0;
    border-radius: 3px;
    background: var(--dz-accent-color);
}

.slider-knob {
    position: absolute;
    top: 50%;
    left: 62%;
    width: 15px;
    height: 15px;
    margin: -7.5px 0 0 -7.5px;
    border-radius: 50%;
    background: var(--dz-accent-color);
    box-shadow: var(--dz-elev-card);
}

/* The dimmer card pairs a percentage with the control. */
.dc-status .dc-level {
    margin-right: var(--site-card-space-sm);
    font-variant-numeric: tabular-nums;
}

/* ---- Features -------------------------------------------------------- */

.feature-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 1rem;
}

.feature-card {
    background: var(--dz-widget-bg);
    color: var(--dz-widget-text);
    border: 1px solid var(--dz-input-border);
    border-radius: var(--site-radius-container);
    box-shadow: var(--dz-elev-card);
    padding: 1.25rem;
}

.feature-card h3 { margin: 0; font-size: var(--site-text-md); }

.feature-card p {
    margin: 0.5rem 0 0;
    font-size: var(--site-text-sm);
    color: var(--secondary-text-color);
}

/* ---- Scheme swatches ------------------------------------------------- */

/* Shown only when the swatch grid cannot be built. Same callout shape as
   .install-note, so a no-JS visitor gets an explanation rather than an empty
   box under a heading that says "click one to apply it". */
.noscript-note {
    width: min(100% - 2.5rem, calc(var(--site-max-width) - 2.5rem));
    margin: 0 auto;
    padding: 0.85rem 1rem;
    background: rgba(var(--dz-accent-values), 0.07);
    border-left: 3px solid var(--dz-accent-color);
    border-radius: var(--site-radius-container);
    font-size: var(--site-text-sm);
    color: var(--secondary-text-color);
}

/* Underlined body text, not accent-coloured text. Accent on this tinted band is
   4.36:1 in Gruvbox Dark, under the AA floor, and a link sitting inside a
   sentence should not be marked by colour alone anyway (WCAG 1.4.1). */
.noscript-note a {
    color: var(--dz-body-text);
    text-decoration: underline;
}

.scheme-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 1rem;
}

.scheme-swatch {
    display: block;
    width: 100%;
    text-align: left;
    cursor: pointer;
    font-family: inherit;
    font-size: var(--site-text-sm);
    font-weight: 600;
    padding: 0.9rem 1rem;
    background: var(--dz-widget-bg);
    color: var(--dz-body-text);
    border: 2px solid var(--dz-input-border);
    border-radius: var(--site-radius-container);
    box-shadow: var(--dz-elev-card);
}

.scheme-swatch[aria-pressed="true"] { border-color: var(--dz-accent-color); }

.scheme-swatch-chips { display: flex; gap: 0.35rem; margin-top: 0.6rem; }

.chip {
    width: 28px;
    height: 20px;
    border-radius: 3px;
    border: 1px solid var(--dz-input-border);
}

.chip-bg { background: var(--dz-body-bg); }
.chip-nav { background: var(--dz-nav-bg); }
.chip-card { background: var(--dz-widget-bg); }
.chip-accent { background: var(--dz-accent-color); }
.chip-text { background: var(--dz-body-text); }

/* ---- Showcase -------------------------------------------------------- */

.showcase-shot img {
    border: 1px solid var(--dz-input-border);
    border-radius: var(--site-radius-container);
    box-shadow: var(--dz-elev-overlay);
    margin: 0 auto;
}

/* ---- Mobile screenshots ---------------------------------------------- */

.phone-grid {
    display: grid;
    grid-template-columns: repeat(6, minmax(0, 1fr));
    gap: 1rem;
}

.phone img {
    border: 1px solid var(--dz-input-border);
    border-radius: var(--site-radius-container);
    box-shadow: var(--dz-elev-card);
}

.phone figcaption {
    margin-top: 0.6rem;
    text-align: center;
    font-size: var(--site-text-xs);
    color: var(--secondary-text-color);
}

/* ---- Install --------------------------------------------------------- */

/* The recommended route is a numbered flow: visible accent step numbers on
   ::before, joined by a hairline, so it reads as one sequence rather than
   four equal options. */
/* Longhands only: the shared container rule above sets margin-left/right to
   auto, and a `margin: 0` shorthand here would silently un-centre the list. */
.install-steps {
    list-style: none;
    margin-top: 0;
    margin-bottom: 0;
    counter-reset: step;
}

.install-steps > li {
    position: relative;
    counter-increment: step;
    padding-left: 3.5rem;
    padding-bottom: 1.75rem;
}

.install-steps > li:last-child { padding-bottom: 0; }

.install-steps > li::before {
    content: counter(step);
    position: absolute;
    left: 0;
    top: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: 50%;
    background: var(--dz-accent-color);
    color: var(--dz-accent-text);
    font-size: var(--site-text-sm);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.install-steps > li:not(:last-child)::after {
    content: "";
    position: absolute;
    left: 1.125rem;
    top: 2.75rem;
    bottom: 0.5rem;
    width: 1px;
    background: var(--dz-input-border);
}

.install-steps h3 {
    margin: 0.25rem 0 0;
    font-size: var(--site-text-lg);
}

.install-steps p {
    margin: 0.35rem 0 0;
    font-size: var(--site-text-sm);
    color: var(--secondary-text-color);
}

/* Notes and the alternatives heading align with the cards' inner edge, not
   the container edge, so the rule and the callout bar line up with the grid
   above them. */
.install-note,
.install-alt-head {
    width: min(100% - 2.5rem, calc(var(--site-max-width) - 2.5rem));
    margin-left: auto;
    margin-right: auto;
}

.install-note {
    margin-top: 1.5rem;
    margin-bottom: 0;
    padding: 0.85rem 1rem;
    background: rgba(var(--dz-accent-values), 0.07);
    border-left: 3px solid var(--dz-accent-color);
    border-radius: var(--site-radius-container);
    font-size: var(--site-text-sm);
    color: var(--secondary-text-color);
}

.install-alt-head {
    margin-top: 3.5rem;
    margin-bottom: 1.5rem;
    padding-top: 2rem;
    border-top: 1px solid var(--dz-input-border);
    font-size: var(--site-text-lg);
}

.install-blocks {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 1rem;
}

.install-card {
    background: var(--dz-widget-bg);
    color: var(--dz-widget-text);
    border: 1px solid var(--dz-input-border);
    border-radius: var(--site-radius-container);
    box-shadow: var(--dz-elev-card);
    padding: 1.25rem;
}

.install-card h3 { margin: 0; font-size: var(--site-text-md); }

.install-card p {
    margin: 0.6rem 0 0;
    font-size: var(--site-text-sm);
    color: var(--secondary-text-color);
}

/* ---- Code blocks ----------------------------------------------------- */

.code-block {
    margin-top: 1rem;
    background: var(--dz-widget-bg);
    border: 1px solid var(--dz-input-border);
    border-radius: var(--site-radius-container);
    box-shadow: var(--dz-elev-card);
    overflow: hidden;
}

/* Inside a card the block would otherwise sit on its own background, so it
   drops to the body surface to stay readable as a distinct block. */
.install-card .code-block {
    background: var(--dz-body-bg);
    box-shadow: none;
}

.code-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.4rem 0.5rem 0.4rem 0.85rem;
    background: rgba(var(--dz-accent-values), 0.08);
    border-bottom: 1px solid var(--dz-input-border);
    font-size: var(--site-text-micro);
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--secondary-text-color);
}

.code-block pre {
    margin: 0;
    padding: 0.85rem;
    overflow-x: auto;
    font-size: var(--site-text-xs);
    line-height: 1.7;
    color: var(--dz-widget-text);
}

.code-block code {
    padding: 0;
    background: transparent;
    font-size: inherit;
}

.copy-btn {
    flex: 0 0 auto;
    cursor: pointer;
    /* The label swaps to "Copied" / "Press Ctrl+C" on click, so it must not
       wrap inside the code head. */
    white-space: nowrap;
    font-family: inherit;
    font-size: var(--site-text-micro);
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    padding: 0.25rem 0.6rem;
    border: 1px solid var(--dz-accent-color);
    border-radius: var(--site-radius-interactive);
    background: transparent;
    /* Same reason as .btn-outline, and worse here: 11px accent text over the
       rgba(accent, 0.08) head is 3.81:1 in Gruvbox Dark. The accent stays as
       the border. */
    color: var(--dz-body-text);
}

.copy-btn:hover {
    background: var(--dz-accent-color);
    color: var(--dz-accent-text);
}

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

.footer {
    background: var(--dz-nav-bg);
    border-top: 1px solid var(--dz-input-border);
    padding-top: 3rem;
    padding-bottom: 2.5rem;
}

.footer-inner {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: 2rem;
}

.footer-logo {
    display: block;
    font-size: var(--site-text-lg);
    font-weight: 600;
}

.footer-brand p {
    margin: 0.25rem 0 0;
    font-size: var(--site-text-sm);
    color: var(--secondary-text-color);
}

.footer-links {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem 1.25rem;
}

.footer-links a {
    font-size: var(--site-text-sm);
    font-weight: 600;
    text-decoration: none;
    color: var(--secondary-text-color);
}

.footer-links a:hover { color: var(--dz-accent-color); }

.footer-credits {
    width: min(100% - 2.5rem, calc(var(--site-max-width) - 2.5rem));
    margin: 2.5rem auto 0;
    padding: 1.5rem 0 0;
    border-top: 1px solid var(--dz-input-border);
    text-align: center;
    font-size: var(--site-text-xs);
    color: var(--secondary-text-color);
}

/* Same reasoning as .noscript-note a: this one sits inside a sentence too. */
.footer-credits a {
    color: var(--dz-body-text);
    text-decoration: underline;
}

/* ---- Motion: opt-in only -------------------------------------------- */

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

    .nav-links a,
    .btn,
    .device-card,
    .feature-card,
    .scheme-swatch,
    .install-card,
    .copy-btn,
    .footer-links a,
    .phone img {
        transition: background-color 0.2s ease, color 0.2s ease,
                    border-color 0.2s ease, transform 0.2s ease,
                    box-shadow 0.2s ease;
    }

    /* css/switch.css animates both at 0.4s; the knob moves by transform now,
       not by `left`, so the property list has to follow it. */
    .switch { transition: background-color 0.4s ease; }
    .switch-knob { transition: transform 0.4s ease, background-color 0.4s ease; }

    .btn:hover { transform: translateY(-1px); }
    .feature-card:hover { transform: translateY(-3px); box-shadow: var(--dz-elev-overlay); }
    .scheme-swatch:hover { transform: translateY(-2px); box-shadow: var(--dz-elev-popup); }
    .install-card:hover { box-shadow: var(--dz-elev-popup); }
    .phone:hover img { transform: translateY(-3px); box-shadow: var(--dz-elev-popup); }

    .reveal { opacity: 0; transform: translateY(16px); transition: opacity 0.5s ease, transform 0.5s ease; }
    .reveal.is-visible { opacity: 1; transform: none; }
}

/* ---- Responsive ------------------------------------------------------ */

@media (max-width: 980px) {
    .hero {
        grid-template-columns: minmax(0, 1fr);
        gap: 2.5rem;
        padding-top: 3.5rem;
        padding-bottom: 3.5rem;
    }

    .hero h1 { font-size: var(--site-text-display-md); }

    /* The picker is the page's whole point, so it keeps its width and the
       links give theirs up first. */
    .nav-inner { gap: 0.75rem; }
    .wordmark { font-size: var(--site-text-md); }
    .nav-links a { font-size: var(--site-text-xs); padding: 0.3rem 0.45rem; }

    .phone-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (max-width: 768px) {
    main > section {
        padding-top: 3rem;
        padding-bottom: 3rem;
    }

    /* Seven links will not fit beside the picker at this width, and stacking
       them makes the sticky bar eat the viewport. Docs and GitHub stay one
       tap away in the hero and the footer. */
    .nav-links { display: none; }

    .section-head { margin-bottom: 2rem; }

    .install-blocks { grid-template-columns: minmax(0, 1fr); }

    .footer-inner {
        flex-direction: column;
        align-items: flex-start;
        gap: 1.5rem;
    }
}

@media (max-width: 480px) {
    .hero h1 { font-size: var(--site-text-display-sm); }
    .hero-sub { font-size: var(--site-text-sm); }
    .section-head h2 { font-size: var(--site-text-xl); }
    .section-desc { font-size: var(--site-text-sm); }

    .hero-actions {
        flex-direction: column;
        align-items: stretch;
    }

    .hero-actions .btn { text-align: center; }

    .phone-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }

    .install-steps > li { padding-left: 2.9rem; }
    .install-steps > li::before { width: 2rem; height: 2rem; }
    .install-steps > li:not(:last-child)::after { left: 1rem; top: 2.5rem; }
}
