/* ==========================================================================
   Just in time Watches — design system

   Written from scratch. The previous version grew by accretion into 2,000 lines
   of overrides fighting the CMS's Bootstrap; this replaces it with a small,
   deliberate vocabulary that the theme's own templates are built from.

   The colour tokens carry over because they were measured rather than chosen —
   sampled from the reference site, then checked against the WCAG formula. Every
   ratio quoted in a comment here was computed, not estimated.

   Structure
     1  Tokens
     2  Reset and base
     3  Type
     4  Layout
     5  Buttons
     6  Forms
     7  Card
     8  Panel, table, spec list
     9  Header and footer
    10  Motion
    11  Utilities
    12  Responsive
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */

:root {
    /* Surfaces. Cream, sampled from the reference site — its page ground is
       #F8F0E5 and the dark people remember is full-bleed photography over it. */
    --jt-ground:        #F8F0E5;
    --jt-ground-raised: #FAF5ED;
    --jt-paper:         #FFFFFF;
    --jt-hairline:      #E4DACA;

    /* The mid-tone a watch is photographed against. Cream lets a steel case
       disappear; black blows out a gold one. */
    --jt-taupe:         #6E6963;
    --jt-on-taupe:      #FFFFFF;   /* 5.44:1 on taupe */

    /* Ink. Ratios are against the worst ground each token can land on. */
    --jt-ink:           #412C17;   /* body           11.64:1 */
    --jt-ink-strong:    #181818;   /* headings       15.72:1 */
    --jt-ink-muted:     #776251;   /* meta            5.09:1 */
    --jt-ink-faint:     #6F625A;   /* fine print      5.21:1 */
    --jt-ink-inert:     #8F857B;   /* inert control   3.27:1 */

    /* Accent: the royal blue of the boutique's mark. Champagne carried this
       role on the old dark system and measures 1.99:1 on cream — unusable. */
    --jt-accent:        #003E91;   /* links, CTA  8.86:1, white on it 10.01:1 */
    --jt-accent-bright: #1155C4;   /* hover       5.96:1 */
    --jt-accent-deep:   #002A63;
    --jt-focus:         #1155C4;   /* clears the 3:1 non-text indicator floor */

    --jt-signal:        #FD0200;   /* sale marks: fills and large text only */
    --jt-error:         #B3251F;   /* field errors    5.81:1 */

    /* Type. A sans pairing, as the reference site uses. Jost stands in for its
       Prelo, Inter for its Sohne; neither original is licensable here. */
    /* The brand faces, served from public/assets/fonts/vc and declared in
       justintime-home.css. Prelo for display, Sohne for text - the pair the
       reference is set in. Both fall back to a grotesque of similar width, so
       a shop that removes the files gets a page that still reads. */
    --jt-display: Prelo, 'Helvetica Neue', Helvetica, Arial, sans-serif;
    --jt-sans:    'Sohne Buch', 'Helvetica Neue', Helvetica, Arial, sans-serif;

    /* 11-12px / 0.2em / uppercase is the reference site's signature. It is what
       makes a reference number or a price read as a specification. */
    --jt-track: 0.2em;

    /* Space. A 4px base, spacious end of the scale. */
    --jt-1:  4px;
    --jt-2:  8px;
    --jt-3:  12px;
    --jt-4:  16px;
    --jt-5:  24px;
    --jt-6:  32px;
    --jt-7:  48px;
    --jt-8:  64px;
    --jt-9:  96px;
    --jt-10: 128px;

    --jt-gutter: var(--jt-5);
    --jt-max: 1360px;

    /* Motion. Long by web standards — 650ms where most interfaces use 200 —
       and the slowness is the point. */
    --jt-ease:    cubic-bezier(0.22, 1, 0.36, 1);
    --jt-ease-io: cubic-bezier(0.65, 0, 0.35, 1);
    --jt-fast:    200ms;
    --jt-base:    400ms;
    --jt-slow:    650ms;
}

/* --------------------------------------------------------------------------
   2. Reset and base
   -------------------------------------------------------------------------- */

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

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

@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
}

body {
    margin: 0;
    background: var(--jt-ground);
    color: var(--jt-ink);
    font-family: var(--jt-sans);
    font-size: 15px;
    font-weight: 300;
    line-height: 1.65;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

img, svg, video { max-width: 100%; display: block; }

a {
    color: var(--jt-accent);
    text-decoration: none;
    transition: color var(--jt-fast) var(--jt-ease-io);
}

a:hover { color: var(--jt-accent-bright); }

/* Focus is never removed. */
:focus-visible {
    outline: 2px solid var(--jt-focus);
    outline-offset: 2px;
    border-radius: 1px;
}

::selection {
    background: var(--jt-accent);
    color: #fff;
}

hr {
    border: 0;
    border-top: 1px solid var(--jt-hairline);
    margin: var(--jt-6) 0;
}

/* --------------------------------------------------------------------------
   3. Type
   -------------------------------------------------------------------------- */

h1, h2, h3, h4, h5, h6 {
    font-family: var(--jt-display);
    font-weight: 400;
    color: var(--jt-ink-strong);
    letter-spacing: -0.012em;
    margin: 0;
}

/* Fluid, so a headline is proportionate on a laptop and on a 27-inch display
   without a breakpoint for each. The 1.04 line-height is what makes a two-line
   headline read as one object rather than two rows. */
.jt-h1 { font-size: clamp(38px, 5.2vw, 76px); line-height: 1.04; }
.jt-h2 { font-size: clamp(28px, 3.4vw, 46px); line-height: 1.08; }
.jt-h3 { font-size: clamp(22px, 2.2vw, 30px); line-height: 1.15; }
.jt-h4 { font-size: 19px; line-height: 1.25; }

.jt-lead {
    font-size: clamp(16px, 1.3vw, 19px);
    line-height: 1.6;
    color: var(--jt-ink-muted);
    max-width: 54ch;
    margin: 0;
}

/* Long-form copy gets a measure. Without one the container stretches a
   paragraph to 1300px and the eye loses the line between one row and the next. */
.jt-prose {
    max-width: 68ch;
    line-height: 1.75;
}

.jt-prose > * + * { margin-top: var(--jt-4); }
.jt-prose h2, .jt-prose h3 { margin-top: var(--jt-7); }

/* The micro-type: eyebrows, labels, prices, references, nav, buttons. */
.jt-micro,
/* The eyebrow above a section heading.

   12px on 2.4px of tracking, set in Sohne Kraftig - the reference's measured
   values, matched so that a jt- section and a vc- section sitting on the same
   page carry the same label. It was 11px on 2.2px in the theme's own sans,
   which read as a slightly different thing two scroll-lengths apart. */
.jt-eyebrow {
    font-family: 'Sohne Kraftig', var(--jt-sans);
    font-size: 12px;
    font-weight: 400;
    letter-spacing: 2.4px;
    text-transform: uppercase;
    line-height: 13.2px;
}

.jt-eyebrow {
    display: block;
    color: var(--jt-ink-muted);
    margin-bottom: var(--jt-3);
}

.jt-muted { color: var(--jt-ink-muted); }
.jt-faint { color: var(--jt-ink-faint); }

/* Every figure a customer reads gets tabular figures, so a column of prices
   aligns on the decimal. */
.jt-num,
.jt-price {
    font-variant-numeric: tabular-nums;
    font-feature-settings: 'tnum';
}

.jt-price {
    font-family: var(--jt-sans);
    font-size: 12px;
    font-weight: 500;
    letter-spacing: var(--jt-track);
    color: var(--jt-ink-strong);
}

/* --------------------------------------------------------------------------
   4. Layout
   -------------------------------------------------------------------------- */

.jt-wrap {
    width: 100%;
    max-width: var(--jt-max);
    margin-inline: auto;
    padding-inline: var(--jt-gutter);
}

.jt-wrap--narrow { max-width: 860px; }
.jt-wrap--wide   { max-width: 1600px; }

.jt-section { padding-block: var(--jt-9); }
.jt-section--tight { padding-block: var(--jt-7); }
.jt-section--flush-top { padding-top: 0; }

.jt-band {
    background: var(--jt-ground-raised);
    border-block: 1px solid var(--jt-hairline);
}

/* A section opener: eyebrow and heading left, one action right. */
.jt-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--jt-5);
    flex-wrap: wrap;
    margin-bottom: var(--jt-7);
}

.jt-grid {
    display: grid;
    grid-template-columns: repeat(var(--jt-cols, 4), minmax(0, 1fr));
    gap: var(--jt-6) var(--jt-4);
}

.jt-grid--3 { --jt-cols: 3; }
.jt-grid--2 { --jt-cols: 2; }

/* An asymmetric editorial pair. Never 50/50: two equal halves read as a
   comparison rather than as a composition. */
.jt-split {
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    gap: var(--jt-8);
    align-items: center;
}

.jt-split--reverse { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); }

/* --------------------------------------------------------------------------
   5. Buttons

   One filled button per view. Everything else is an outline or a bare link.
   -------------------------------------------------------------------------- */

.jt-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--jt-2);
    min-height: 48px;              /* comfortably over the 44px target floor */
    padding: 14px 30px;
    border: 1px solid transparent;
    border-radius: 0;
    background: none;
    font-family: var(--jt-sans);
    font-size: 11px;
    font-weight: 500;
    letter-spacing: var(--jt-track);
    text-transform: uppercase;
    text-align: center;
    cursor: pointer;
    transition: background-color var(--jt-base) var(--jt-ease-io),
                border-color var(--jt-base) var(--jt-ease-io),
                color var(--jt-base) var(--jt-ease-io),
                transform var(--jt-fast) var(--jt-ease);
}

.jt-btn--primary {
    background: var(--jt-accent);
    border-color: var(--jt-accent);
    color: #fff;
}

.jt-btn--primary:hover {
    background: var(--jt-accent-bright);
    border-color: var(--jt-accent-bright);
    color: #fff;
}

.jt-btn--outline {
    border-color: var(--jt-ink);
    color: var(--jt-ink);
}

.jt-btn--outline:hover {
    background: var(--jt-ink);
    color: var(--jt-ground);
}

.jt-btn--quiet {
    padding-inline: 0;
    min-height: 0;
    color: var(--jt-ink);
    border: 0;
}

.jt-btn--block { width: 100%; }

/* The press. One pixel of travel — enough to feel, not enough to notice. */
.jt-btn:active { transform: translateY(1px); }

.jt-btn:disabled,
.jt-btn[aria-disabled='true'] {
    opacity: 0.45;
    cursor: not-allowed;
    transform: none;
}

/* A link that draws its underline rather than switching colour. */
.jt-link {
    position: relative;
    color: var(--jt-ink);
    padding-bottom: 2px;
}

.jt-link::after {
    content: '';
    position: absolute;
    inset-inline: 0;
    bottom: 0;
    height: 1px;
    background: currentColor;
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--jt-slow) var(--jt-ease-io);
}

.jt-link:hover::after,
.jt-link[aria-current='page']::after { transform: scaleX(1); }

/* --------------------------------------------------------------------------
   6. Forms
   -------------------------------------------------------------------------- */

.jt-field { margin-bottom: var(--jt-5); }

.jt-field > label,
.jt-label {
    display: block;
    margin-bottom: var(--jt-2);
    font-size: 11px;
    font-weight: 500;
    letter-spacing: var(--jt-track);
    text-transform: uppercase;
    color: var(--jt-ink-muted);
}

/* The asterisk is the accent, so "required" is not signalled by shape alone at
   the size the label is set. */
.jt-field label span[aria-hidden] { color: var(--jt-accent); }

.jt-input,
.jt-field input:not([type='checkbox']):not([type='radio']),
.jt-field select,
.jt-field textarea {
    width: 100%;
    min-height: 48px;
    padding: 12px 14px;
    background: var(--jt-paper);
    border: 1px solid var(--jt-hairline);
    border-radius: 0;
    color: var(--jt-ink);
    font-family: var(--jt-sans);
    font-size: 15px;
    font-weight: 300;
    transition: border-color var(--jt-base) var(--jt-ease-io);
}

.jt-field textarea { min-height: 132px; line-height: 1.6; resize: vertical; }

.jt-field input:hover,
.jt-field select:hover,
.jt-field textarea:hover { border-color: var(--jt-ink-faint); }

.jt-field input:focus,
.jt-field select:focus,
.jt-field textarea:focus { border-color: var(--jt-ink); }

::placeholder { color: var(--jt-ink-faint); opacity: 1; }

.jt-help {
    margin: var(--jt-2) 0 0;
    font-size: 12px;
    color: var(--jt-ink-muted);
}

/* Errors are never carried by colour alone: the message sits under its field,
   is prefixed with a mark, and the input carries aria-invalid. */
.jt-error {
    margin: var(--jt-2) 0 0;
    font-size: 12px;
    color: var(--jt-error);
}

.jt-error::before { content: '— '; }

[aria-invalid='true'] { border-color: var(--jt-error) !important; }

.jt-field-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--jt-4);
}

/* The native select arrow cannot be styled, so the control supplies its own. */
.jt-field select {
    appearance: none;
    background-image:
        linear-gradient(45deg, transparent 50%, var(--jt-ink-muted) 50%),
        linear-gradient(135deg, var(--jt-ink-muted) 50%, transparent 50%);
    background-position: calc(100% - 19px) center, calc(100% - 13px) center;
    background-size: 6px 6px;
    background-repeat: no-repeat;
    padding-right: 42px;
}

.jt-notice {
    padding: var(--jt-4) var(--jt-5);
    border-left: 2px solid var(--jt-accent);
    background: var(--jt-ground-raised);
    font-size: 14px;
    margin-bottom: var(--jt-5);
}

.jt-notice--error { border-left-color: var(--jt-error); }

/* --------------------------------------------------------------------------
   7. Card

   A photograph on a plate, a name, a price. No border, no shadow, no badge.
   The only motion is the photograph breathing inside a frame that never moves,
   so a grid never reflows on hover.
   -------------------------------------------------------------------------- */

.jt-card {
    display: block;
    color: inherit;
}

.jt-card__media {
    display: block;
    position: relative;
    overflow: hidden;
    background: var(--jt-ground-raised);
    /* 1:1.41 — the reference site's own product frame. Fixed, so watches shot
       at different proportions still line up. */
    aspect-ratio: 1 / 1.41;
}

.jt-card__media img {
    position: relative;
    width: 100%;
    height: 100%;
    object-fit: contain;
    padding: 12%;
    /* A shadow cast by the watch's own outline - the cut-out is transparent,
       so this follows the case and the bracelet rather than a rectangle. */
    filter: drop-shadow(0 0 22px color-mix(in srgb, var(--jt-glow, #e8c89a) 55%, transparent))
            drop-shadow(0 18px 18px rgba(40, 28, 16, 0.14));
    transition: transform var(--jt-slow) var(--jt-ease), filter var(--jt-slow) var(--jt-ease);
}

/* The watch's colour as light behind it. --jt-glow is set per card from the
   watch's cut-out; the default is the house champagne. */
.jt-card__glow {
    position: absolute;
    inset: 6% -10%;
    border-radius: 50%;
    background: radial-gradient(closest-side,
        color-mix(in srgb, var(--jt-glow, #e8c89a) 70%, transparent) 0%,
        color-mix(in srgb, var(--jt-glow, #e8c89a) 38%, transparent) 50%,
        transparent 100%);
    filter: blur(24px);
    opacity: 0.85;
    transform: scale(0.92);
    transition: opacity var(--jt-slow) var(--jt-ease), transform var(--jt-slow) var(--jt-ease);
    pointer-events: none;
}

/* A thin band of light that crosses the frame once on hover, the way a
   showcase light catches glass. */
.jt-card__media::after {
    content: '';
    position: absolute;
    inset: -20% auto -20% -60%;
    width: 40%;
    background: linear-gradient(100deg, transparent 0%, rgba(255, 255, 255, 0.38) 50%, transparent 100%);
    transform: skewX(-18deg) translateX(0);
    opacity: 0;
    pointer-events: none;
}

@media (hover: hover) {
    .jt-card:hover .jt-card__glow { opacity: 1; }
}

@media (hover: hover) and (prefers-reduced-motion: no-preference) {
    .jt-card:hover .jt-card__media img {
        transform: translateY(-10px) scale(1.04);
        filter: drop-shadow(0 0 34px color-mix(in srgb, var(--jt-glow, #e8c89a) 80%, transparent))
                drop-shadow(0 30px 26px rgba(40, 28, 16, 0.2));
    }
    .jt-card:hover .jt-card__glow { transform: scale(1.08); }
    .jt-card:hover .jt-card__media::after { animation: jt-card-sheen 1100ms var(--jt-ease) both; }
}

@keyframes jt-card-sheen {
    0%   { opacity: 0; transform: skewX(-18deg) translateX(0); }
    20%  { opacity: 1; }
    100% { opacity: 0; transform: skewX(-18deg) translateX(420%); }
}

.jt-card__body {
    display: block;
    padding-top: var(--jt-4);
    text-align: center;
}

.jt-card__brand {
    display: block;
    font-size: 10px;
    font-weight: 500;
    letter-spacing: var(--jt-track);
    text-transform: uppercase;
    color: var(--jt-ink-muted);
    margin-bottom: var(--jt-1);
}

.jt-card__name {
    display: block;
    font-family: var(--jt-display);
    font-size: 15px;
    color: var(--jt-ink-strong);
    line-height: 1.3;
    margin-bottom: var(--jt-2);
    transition: color var(--jt-fast) var(--jt-ease-io);
}

.jt-card:hover .jt-card__name { color: var(--jt-accent); }

/* --------------------------------------------------------------------------
   8. Panel, table, spec list
   -------------------------------------------------------------------------- */

.jt-panel {
    background: var(--jt-paper);
    border: 1px solid var(--jt-hairline);
    padding: var(--jt-6);
}

.jt-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
}

.jt-table th {
    text-align: left;
    padding: var(--jt-3) var(--jt-4) var(--jt-3) 0;
    border-bottom: 1px solid var(--jt-hairline);
    font-size: 11px;
    font-weight: 500;
    letter-spacing: var(--jt-track);
    text-transform: uppercase;
    color: var(--jt-ink-muted);
}

.jt-table td {
    padding: var(--jt-4) var(--jt-4) var(--jt-4) 0;
    border-bottom: 1px solid var(--jt-hairline);
    font-variant-numeric: tabular-nums;
    vertical-align: middle;
}

/* The specification list. Up to 46 fields per watch are imported, so this is a
   real part of the product page rather than an afterthought. */
.jt-spec-list {
    display: grid;
    grid-template-columns: minmax(150px, 32%) 1fr;
    margin: 0;
    font-size: 14px;
}

.jt-spec-list dt,
.jt-spec-list dd {
    margin: 0;
    padding: var(--jt-3) var(--jt-4) var(--jt-3) 0;
    border-top: 1px solid var(--jt-hairline);
}

.jt-spec-list dt {
    font-size: 11px;
    font-weight: 500;
    letter-spacing: var(--jt-track);
    text-transform: uppercase;
    color: var(--jt-ink-muted);
}

.jt-spec-list dd { color: var(--jt-ink); font-variant-numeric: tabular-nums; }

/* An empty state is a place to go, not a blank screen. */
.jt-empty {
    text-align: center;
    padding: var(--jt-9) var(--jt-5);
}

.jt-empty h1, .jt-empty h2 { margin-bottom: var(--jt-3); }
.jt-empty p { margin: 0 auto var(--jt-6); max-width: 42ch; color: var(--jt-ink-muted); }

/* --------------------------------------------------------------------------
   9. Header and footer
   -------------------------------------------------------------------------- */

.jt-header {
    position: sticky;
    top: 0;
    z-index: 60;
    background: var(--jt-ground);
    border-bottom: 1px solid var(--jt-hairline);
    transition: background-color var(--jt-base) var(--jt-ease-io),
                box-shadow var(--jt-base) var(--jt-ease-io);
}

.jt-header__bar {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: var(--jt-5);
    max-width: var(--jt-max);
    margin-inline: auto;
    padding-inline: var(--jt-gutter);
    min-height: 84px;
}

.jt-header.is-condensed {
    background: rgba(248, 240, 229, 0.92);
    backdrop-filter: saturate(150%) blur(14px);
    -webkit-backdrop-filter: saturate(150%) blur(14px);
    box-shadow: 0 1px 0 var(--jt-hairline);
}

.jt-header.is-condensed .jt-header__bar { min-height: 62px; }

.jt-nav {
    display: flex;
    align-items: center;
    gap: var(--jt-5);
}

.jt-mark { display: flex; align-items: center; justify-content: center; }

.jt-mark img {
    height: 50px;
    width: auto;
    object-fit: contain;
    /* Condensed by scale, not by height: animating height reflowed the header
       on every frame of the change. The bar's own height snaps; the mark's
       easing is what the eye follows. */
    transform-origin: center;
    transition: transform var(--jt-base) var(--jt-ease);
}

.jt-header.is-condensed .jt-mark img { transform: scale(0.84); }

.jt-tools {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--jt-3);
}

.jt-icon-btn {
    position: relative;
    width: 44px;
    height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 0;
    background: none;
    color: var(--jt-ink);
    cursor: pointer;
    transition: color var(--jt-fast) var(--jt-ease-io);
}

.jt-icon-btn:hover { color: var(--jt-accent); }

.jt-badge {
    position: absolute;
    top: 5px;
    right: 3px;
    min-width: 16px;
    height: 16px;
    padding-inline: 4px;
    border-radius: 8px;
    background: var(--jt-accent);
    color: #fff;
    font-size: 10px;
    font-weight: 500;
    line-height: 16px;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.jt-footer {
    background: var(--jt-ground);
    border-top: 1px solid var(--jt-hairline);
    padding-block: var(--jt-9) var(--jt-6);
    color: var(--jt-ink-muted);
    font-size: 13px;
}

.jt-footer a { color: var(--jt-ink-muted); }
.jt-footer a:hover { color: var(--jt-accent); }

.jt-footer__cols {
    display: grid;
    grid-template-columns: minmax(0, 2fr) repeat(3, minmax(0, 1fr));
    gap: var(--jt-7);
    margin-bottom: var(--jt-8);
}

.jt-footer h2 {
    font-family: var(--jt-sans);
    font-size: 11px;
    font-weight: 500;
    letter-spacing: var(--jt-track);
    text-transform: uppercase;
    color: var(--jt-ink-muted);
    margin-bottom: var(--jt-4);
}

.jt-footer ul { list-style: none; margin: 0; padding: 0; }
.jt-footer li + li { margin-top: var(--jt-2); }

.jt-footer__base {
    display: flex;
    justify-content: space-between;
    gap: var(--jt-5);
    flex-wrap: wrap;
    padding-top: var(--jt-5);
    border-top: 1px solid var(--jt-hairline);
    font-size: 12px;
}

/* Keyboard-first: the first tab stop jumps past the header. */
.jt-skip {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 200;
    padding: 12px 20px;
    background: var(--jt-accent);
    color: #fff;
    font-size: 11px;
    letter-spacing: var(--jt-track);
    text-transform: uppercase;
}

.jt-skip:focus { left: var(--jt-2); top: var(--jt-2); }

/* --------------------------------------------------------------------------
   10. Motion

   Scoped to html[data-jt-motion], which the inline pre-flight sets only after
   confirming the browser can animate AND that reduced motion is not requested —
   so under reduce-motion none of this exists and the page renders finished
   rather than blank.
   -------------------------------------------------------------------------- */

[data-jt-motion] [data-jt-in] {
    opacity: 0;
    transform: translateY(12px);
}

[data-jt-motion] [data-jt-in].is-in {
    opacity: 1;
    transform: none;
    transition: opacity var(--jt-slow) var(--jt-ease),
                transform var(--jt-slow) var(--jt-ease);
}

/* A card arrives in two beats: the plate fades up with the rest, then the
   watch settles into it from slightly larger and its light blooms behind it -
   the moment a piece is set down in a lit case. Inherits the card's stagger
   delay, so a row still arrives left to right. */
[data-jt-motion] .jt-card[data-jt-in] .jt-card__media img {
    opacity: 0;
    transform: translateY(14px) scale(1.06);
}

[data-jt-motion] .jt-card[data-jt-in] .jt-card__glow {
    opacity: 0;
    transform: scale(0.6);
}

[data-jt-motion] .jt-card[data-jt-in].is-in .jt-card__media img {
    opacity: 1;
    transform: none;
    transition: opacity 900ms var(--jt-ease) 120ms,
                transform 1100ms var(--jt-ease) 120ms,
                filter var(--jt-slow) var(--jt-ease);
    transition-delay: inherit;
}

[data-jt-motion] .jt-card[data-jt-in].is-in .jt-card__glow {
    opacity: 0.85;
    transform: scale(0.92);
    transition: opacity 1400ms var(--jt-ease) 300ms, transform 1600ms var(--jt-ease) 300ms;
}

@media (hover: hover) {
    [data-jt-motion] .jt-card[data-jt-in].is-in:hover .jt-card__glow { opacity: 1; transform: scale(1.08); transition-delay: 0ms; }
    [data-jt-motion] .jt-card[data-jt-in].is-in:hover .jt-card__media img {
        transform: translateY(-10px) scale(1.04);
        filter: drop-shadow(0 0 34px color-mix(in srgb, var(--jt-glow, #e8c89a) 80%, transparent))
                drop-shadow(0 30px 26px rgba(40, 28, 16, 0.2));
        transition: transform var(--jt-slow) var(--jt-ease), filter var(--jt-slow) var(--jt-ease);
    }
}

/* --------------------------------------------------------------------------
   11. Utilities
   -------------------------------------------------------------------------- */

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

.jt-stack > * + * { margin-top: var(--jt-4); }
.jt-stack--tight > * + * { margin-top: var(--jt-2); }
.jt-stack--loose > * + * { margin-top: var(--jt-6); }

.jt-center { text-align: center; }
.jt-center .jt-lead { margin-inline: auto; }

.jt-rule { border-top: 1px solid var(--jt-hairline); }

/* --------------------------------------------------------------------------
   12. Responsive

   Four breakpoints, which are the four widths the design was drawn at.
   -------------------------------------------------------------------------- */

@media (max-width: 1200px) {
    .jt-grid { --jt-cols: 3; }
    .jt-split, .jt-split--reverse { gap: var(--jt-6); }
}

@media (max-width: 900px) {
    :root { --jt-gutter: var(--jt-4); }

    .jt-grid { --jt-cols: 2; }

    .jt-split,
    .jt-split--reverse {
        grid-template-columns: 1fr;
        gap: var(--jt-6);
    }

    /* On one column the image belongs above the copy regardless of source
       order, so a reversed pair does not put the picture last. */
    .jt-split--reverse > :first-child { order: 2; }

    .jt-section { padding-block: var(--jt-8); }
    .jt-header__bar { min-height: 64px; }
    .jt-mark img { height: 40px; max-width: 100%; }
    .jt-footer__cols { grid-template-columns: 1fr 1fr; gap: var(--jt-6); }
    .jt-field-row { grid-template-columns: 1fr; }
}

@media (max-width: 560px) {
    .jt-grid { --jt-cols: 2; gap: var(--jt-5) var(--jt-3); }
    .jt-section { padding-block: var(--jt-7); }
    .jt-panel { padding: var(--jt-5); }
    .jt-footer__cols { grid-template-columns: 1fr; }
    .jt-spec-list { grid-template-columns: 1fr; }
    .jt-spec-list dd { border-top: 0; padding-top: 0; }
    .jt-head { margin-bottom: var(--jt-5); }
    .jt-nav { gap: var(--jt-4); }
}

/* --------------------------------------------------------------------------
   13. Homepage pieces

   Two components that only the homepage uses. They live here rather than in a
   page stylesheet because there is one stylesheet, and a component used once
   today is used twice tomorrow.
   -------------------------------------------------------------------------- */

/* The opening piece: one watch, large, on the taupe ground it is photographed
   against, with its brand and price on a line beneath. */
.jt-hero-piece {
    display: block;
    position: relative;
    /* Every stop is dark enough for white to clear 4.5:1 on it, so a label can
       sit anywhere on this gradient rather than only where it happens to now.
       The old lightest stop was #837D76 at 4.07:1. */
    background:
        radial-gradient(120% 100% at 50% 38%,
            #7A746E 0%, var(--jt-taupe) 58%, #5C5751 100%);
    aspect-ratio: 1 / 1;
    overflow: hidden;
}

.jt-hero-piece img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    padding: 14%;
    filter: drop-shadow(0 30px 40px rgba(0, 0, 0, 0.38));
    transition: transform var(--jt-slow) var(--jt-ease);
}

@media (hover: hover) and (prefers-reduced-motion: no-preference) {
    .jt-hero-piece:hover img { transform: scale(1.03); }
}

.jt-hero-piece__label {
    position: absolute;
    left: var(--jt-5);
    bottom: var(--jt-5);
    color: var(--jt-on-taupe);
}

/* A collection tile: image, with its name laid over the lower edge. */
.jt-collection {
    display: block;
    position: relative;
    overflow: hidden;
    background: var(--jt-ground-raised);
    aspect-ratio: 4 / 5;
    color: inherit;
}

.jt-collection__media {
    display: block;
    width: 100%;
    height: 100%;
}

.jt-collection__media img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    padding: 16%;
    transition: transform var(--jt-slow) var(--jt-ease);
}

/* A photograph that has not had its ground removed fills the tile instead of
   floating in it.

   contain plus padding is right for a cut-out: the watch sits on the tile's
   own cream, which is the treatment the rest of the site uses. Run on a
   photograph that still has its ground, it puts a small rectangle of someone
   else's background in the middle of a large cream square - which is what the
   one collection shot on black looked like, and it read as a broken image
   rather than a design.

   Filling the tile is not as good as a cut-out, but it is never broken: the
   photograph becomes the tile, and the name sits on the gradient over it. */
.jt-collection--fill .jt-collection__media img {
    object-fit: cover;
    padding: 0;
}

/* Over a photograph rather than over cream, so the gradient has to carry the
   contrast itself - and the name goes light, because a filled tile is as
   likely to be dark as not. */
.jt-collection--fill .jt-collection__name {
    color: var(--vc-white, #fff);
    background: linear-gradient(to top,
        rgba(10, 10, 12, 0.86) 0%,
        rgba(10, 10, 12, 0.62) 46%,
        rgba(10, 10, 12, 0) 100%);
}

.jt-collection--fill .jt-collection__name .jt-faint { color: rgba(255, 255, 255, 0.72); }

@media (hover: hover) and (prefers-reduced-motion: no-preference) {
    .jt-collection:hover .jt-collection__media img { transform: scale(1.04); }
}

/* The name sits on a gradient rather than on the image, so it stays readable
   whatever the photograph behind it happens to be. */
.jt-collection__name {
    position: absolute;
    inset-inline: 0;
    bottom: 0;
    padding: var(--jt-8) var(--jt-5) var(--jt-5);
    color: var(--jt-ink-strong);
    background: linear-gradient(to top,
        var(--jt-ground) 0%,
        rgba(248, 240, 229, 0.92) 55%,
        rgba(248, 240, 229, 0) 100%);
}

@media (max-width: 900px) {
    .jt-hero-piece { aspect-ratio: 4 / 3; }
}

/* --------------------------------------------------------------------------
   14. The product page

   The piece on the ground it is photographed against, with a panel floating
   over its lower left. The overlap is the point: two rectangles side by side
   read as a form; one laid over the other reads as an object with a label.
   -------------------------------------------------------------------------- */

.jt-piece {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: min(86vh, 880px);
    margin-bottom: var(--jt-9);
    overflow: hidden;
    /* The reference's grey, at its measured angle and stops.

       This only works because the packshots are cut out - scripts/make_cutouts.py
       floods the white background out of every photograph and writes a
       transparent PNG beside it. Put an uncut JPEG here and you get a white
       rectangle floating on the slab, which is what this looked like before
       the cut-outs existed. The template falls back to the original wherever
       a cut-out is missing, so the page degrades to a white box rather than
       to nothing. */
    background:
        linear-gradient(121deg, rgb(88, 85, 81) 13.39%, rgb(118, 114, 111) 97.29%);
}

.jt-piece__stage {
    position: relative;
    width: min(50vw, 600px);
    height: min(76vh, 760px);
    display: flex;
    align-items: center;
    justify-content: center;
    /* Pushed right so the panel has room without covering the piece. */
    margin-left: min(18vw, 220px);
}

.jt-piece__image {
    max-width: 100%;
    max-height: 100%;
    width: auto;
    object-fit: contain;

    /* drop-shadow, not box-shadow: it traces the image's alpha, so on a cut-out
       it follows the watch. On an uncut JPEG it traces the rectangle instead -
       which is why a product without a cut-out still looks wrong here, and why
       make_cutouts.py is worth running after an import. */
    filter: drop-shadow(0 40px 50px rgba(0, 0, 0, 0.42));
    transition: opacity var(--jt-base) var(--jt-ease);
}

.jt-piece__panel {
    position: absolute;
    left: max(4vw, 24px);
    bottom: max(4vw, 24px);
    width: min(400px, 88vw);
    padding: var(--jt-6);
    background: var(--jt-paper);
    z-index: 2;
    /* A real shadow, unlike anywhere else in the system: this panel IS floating
       over a photograph and needs to say so. */
    box-shadow: 0 30px 70px rgba(0, 0, 0, 0.26);
}

/* Alternate views, down the right edge of the stage. */
/* .jt-piece__views and .jt-view are gone with the hero's thumbnail column.
   They shared their position with the circular controls, so the two drew on
   top of each other; the gallery further down the page shows the other
   photographs now. */

/* Quantity */
.jt-qty {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--jt-4);
    margin-bottom: var(--jt-4);
}

.jt-qty label { margin: 0; }

.jt-qty__control {
    display: flex;
    align-items: stretch;
    border: 1px solid var(--jt-hairline);
}

.jt-qty__btn {
    width: 42px;
    min-height: 42px;
    border: 0;
    background: none;
    color: var(--jt-ink);
    font-size: 16px;
    line-height: 1;
    cursor: pointer;
    transition: background-color var(--jt-fast) var(--jt-ease-io);
}

.jt-qty__btn:hover { background: var(--jt-ground); }

.jt-qty__input {
    width: 54px;
    min-height: 42px !important;
    padding: 0 !important;
    text-align: center;
    border: 0 !important;
    border-inline: 1px solid var(--jt-hairline) !important;
    background: transparent !important;
    font-variant-numeric: tabular-nums;
    /* The spinner arrows duplicate the buttons either side of it. */
    -moz-appearance: textfield;
    appearance: textfield;
}

.jt-qty__input::-webkit-outer-spin-button,
.jt-qty__input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* The imported description leads with a line naming the reference and a
   "Specification" heading. Both duplicate what the section already says. */
.jt-spec-body > p:first-child,
.jt-spec-body > h3 { display: none; }

@media (max-width: 1024px) {
    .jt-piece {
        flex-direction: column;
        min-height: 0;
        padding-top: var(--jt-7);
        margin-bottom: var(--jt-8);
    }

    .jt-piece__stage {
        width: 100%;
        height: min(56vh, 440px);
        margin-left: 0;
    }

    /* Overlapping a photograph on a phone leaves neither readable. */
    .jt-piece__panel {
        position: static;
        width: 100%;
        box-shadow: none;
        padding: var(--jt-6) var(--jt-5);
    }

}

/* --------------------------------------------------------------------------
   15. Service pages

   The studio's four stages, and the appointment page's two columns.
   -------------------------------------------------------------------------- */

/* Four stages across. A hairline above each rather than four boxes in a row —
   boxes would read as a pricing table, which is the wrong genre entirely. */
.jt-stages {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--jt-6);
}

.jt-stage {
    border-top: 1px solid var(--jt-hairline);
    padding-top: var(--jt-5);
}

.jt-stage__n {
    display: block;
    font-family: var(--jt-display);
    font-size: 13px;
    letter-spacing: 0.28em;
    color: var(--jt-accent);
    margin-bottom: var(--jt-4);
    font-variant-numeric: tabular-nums;
}

/* The appointment form beside the boutique's details. */
.jt-appointment {
    display: grid;
    grid-template-columns: minmax(0, 1.4fr) minmax(0, 0.6fr);
    gap: var(--jt-8);
    align-items: start;
}

/* Sticky only where there is room for it to travel. */
.jt-appointment__aside { position: sticky; top: 120px; }

.jt-hours {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: var(--jt-2) var(--jt-4);
    margin: 0;
    font-size: 13px;
}

.jt-hours dt { color: var(--jt-ink-muted); }
.jt-hours dd {
    margin: 0;
    text-align: right;
    color: var(--jt-ink);
    font-variant-numeric: tabular-nums;
}

.jt-form { max-width: 58ch; }

@media (max-width: 1024px) {
    .jt-stages { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .jt-appointment { grid-template-columns: 1fr; gap: var(--jt-7); }
    .jt-appointment__aside { position: static; }
}

@media (max-width: 560px) {
    .jt-stages { grid-template-columns: 1fr; gap: var(--jt-5); }
}

/* --------------------------------------------------------------------------
   16. The collection

   A listing page for a boutique is not a search results page. It opens by
   naming what you are looking at, states how many pieces there are, and then
   gets out of the way. The controls sit on one hairline rule and read as type,
   not as widgets: three questions — which maker, roughly what price, in what
   order — behind a single Refine disclosure.
   -------------------------------------------------------------------------- */

.jt-listing-head {
    text-align: center;
    max-width: 40ch;
    margin: 0 auto var(--jt-7);
}

.jt-listing-head .jt-h1 { margin: 0; }

/* The control bar. Rules above and below, so it reads as a band between the
   title and the grid rather than as a floating toolbar. */
.jt-filters {
    border-block: 1px solid var(--jt-hairline);
    margin-bottom: var(--jt-7);
}

.jt-filters__bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--jt-5);
    flex-wrap: wrap;
    /* No vertical padding: the 44px minimum on the controls inside sets the
       height of the bar, so the rules never sit closer to one control than
       to another. */
    min-height: 56px;
}

.jt-filters__count { margin: 0; }

.jt-filters__actions {
    display: flex;
    align-items: center;
    gap: var(--jt-6);
}

/* Refine and Sort are peers, so they are set identically — the difference
   between them is what they do, not how loud they are. */
.jt-filters__toggle {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0;
    background: none;
    border: 0;
    font-family: var(--jt-sans);
    font-size: 11px;
    font-weight: 500;
    letter-spacing: var(--jt-track);
    text-transform: uppercase;
    color: var(--jt-ink);
    cursor: pointer;
}

/* The rule under Refine is drawn, not an underline, so it can grow from the
   left instead of appearing all at once. */
.jt-filters__toggle::after {
    content: '';
    display: block;
    position: absolute;
    left: 0;
    bottom: 12px;
    width: 100%;
    height: 1px;
    background: currentColor;
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--jt-base) var(--jt-ease);
}

.jt-filters__toggle { position: relative; }
.jt-filters__toggle:hover::after,
.jt-filters__toggle[aria-expanded='true']::after { transform: scaleX(1); }

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

.jt-filters__sort label { margin: 0; white-space: nowrap; }

/* A select that reads as a line of type. The native arrow cannot be styled, so
   the control draws its own — the same pair of gradients the boxed fields use,
   kept identical so the two never look like different components. */
.jt-select {
    appearance: none;
    min-height: 44px;
    padding: 0 22px 0 0;
    background: none;
    background-image:
        linear-gradient(45deg, transparent 50%, var(--jt-ink) 50%),
        linear-gradient(135deg, var(--jt-ink) 50%, transparent 50%);
    background-position: calc(100% - 6px) center, calc(100% - 1px) center;
    background-size: 5px 5px;
    background-repeat: no-repeat;
    border: 0;
    color: var(--jt-ink);
    font-family: var(--jt-sans);
    font-size: 13px;
    cursor: pointer;
}

.jt-select--block {
    width: 100%;
    min-height: 48px;
    padding: 12px 42px 12px 14px;
    background-color: var(--jt-paper);
    background-position: calc(100% - 19px) center, calc(100% - 13px) center;
    background-size: 6px 6px;
    border: 1px solid var(--jt-hairline);
    font-size: 15px;
    font-weight: 300;
    transition: border-color var(--jt-base) var(--jt-ease-io);
}

.jt-select--block:hover { border-color: var(--jt-ink-faint); }
.jt-select--block:focus { border-color: var(--jt-ink); }

/* The disclosure. hidden means display:none, so a height transition would
   never run — the panel arrives with one short keyframe instead, and under
   reduced motion (where [data-jt-motion] is absent) it simply appears. */
.jt-refine {
    border-top: 1px solid var(--jt-hairline);
    padding-block: var(--jt-5);
}

[data-jt-motion] .jt-refine:not([hidden]) {
    animation: jt-refine-in var(--jt-base) var(--jt-ease) both;
}

@keyframes jt-refine-in {
    from { opacity: 0; transform: translateY(-6px); }
    to   { opacity: 1; transform: none; }
}

.jt-refine__grid {
    display: grid;
    grid-template-columns: minmax(200px, 0.8fr) minmax(0, 2fr) auto;
    gap: var(--jt-5) var(--jt-7);
    align-items: end;
}

/* The collections, as chips: a row of names to press rather than a field to
   open, because there are three of them and pressing one is the whole
   interaction. */
.jt-chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--jt-2);
}

.jt-chip {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0 var(--jt-4);
    border: 1px solid var(--jt-hairline);
    background: var(--jt-ground);
    color: var(--jt-ink);
    font-family: var(--jt-sans);
    font-size: 12px;
    font-weight: 500;
    letter-spacing: var(--jt-track);
    text-transform: uppercase;
    text-decoration: none;
    transition: border-color var(--jt-fast) var(--jt-ease-io),
                color var(--jt-fast) var(--jt-ease-io),
                background var(--jt-fast) var(--jt-ease-io);
}

.jt-chip:hover { border-color: var(--jt-ink); color: var(--jt-ink-strong); }

.jt-chip[aria-current='true'] {
    background: var(--jt-ink-strong);
    border-color: var(--jt-ink-strong);
    color: var(--jt-ground);
}

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

.jt-refine__clear {
    grid-column: 1 / -1;
    display: flex;
    justify-content: flex-end;
}

.jt-refine__grid .jt-field { margin-bottom: 0; }

.jt-refine__price { border: 0; padding: 0; margin: 0; min-width: 0; }
.jt-refine__price legend { padding: 0; }

.jt-refine__range {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    align-items: center;
    gap: var(--jt-3);
}

.jt-refine__apply {
    display: flex;
    align-items: center;
    gap: var(--jt-5);
}

/* Listing grids breathe more than homepage grids: a page of forty watches
   needs the rows to separate, or it reads as wallpaper. */
.jt-grid--listing { gap: var(--jt-8) var(--jt-5); }

.jt-empty__actions {
    display: flex;
    gap: var(--jt-3);
    justify-content: center;
    flex-wrap: wrap;
}

/* --------------------------------------------------------------------------
   17. Pagination
   -------------------------------------------------------------------------- */

.jt-pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--jt-2);
    flex-wrap: wrap;
    margin-top: var(--jt-9);
    padding-top: var(--jt-7);
    border-top: 1px solid var(--jt-hairline);
}

.jt-pagination__numbers {
    display: flex;
    align-items: center;
    gap: var(--jt-1);
}

.jt-page {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* 44px square: the smallest target that can be hit reliably with a thumb,
       which matters more here than anywhere else on the page because these
       sit in a row of near-identical numerals. */
    min-width: 44px;
    height: 44px;
    padding-inline: var(--jt-2);
    color: var(--jt-ink-muted);
    font-size: 13px;
    font-variant-numeric: tabular-nums;
    text-decoration: none;
    transition: color var(--jt-fast) var(--jt-ease-io);
}

.jt-page--edge {
    padding-inline: var(--jt-4);
    font-size: 11px;
    font-weight: 500;
    letter-spacing: var(--jt-track);
    text-transform: uppercase;
}

a.jt-page:hover { color: var(--jt-accent); }

/* The current page is marked by a rule as well as by weight and colour, so it
   is not distinguished by colour alone. */
.jt-page.is-current {
    color: var(--jt-ink-strong);
    font-weight: 500;
    box-shadow: inset 0 -1px 0 0 var(--jt-ink-strong);
}

.jt-page.is-disabled { color: var(--jt-ink-inert); }
.jt-page.is-gap { min-width: 0; padding-inline: var(--jt-1); }

@media (max-width: 900px) {
    .jt-refine__grid { grid-template-columns: 1fr; align-items: stretch; }
    .jt-refine__apply { justify-content: space-between; }
    .jt-grid--listing { gap: var(--jt-7) var(--jt-4); }
}

@media (max-width: 560px) {
    .jt-filters__bar { min-height: 0; padding-block: var(--jt-2); }
    .jt-filters__actions { gap: var(--jt-5); width: 100%; justify-content: space-between; }
    .jt-listing-head { margin-bottom: var(--jt-6); }
    .jt-page--edge { padding-inline: var(--jt-2); }
}

/* --------------------------------------------------------------------------
   18. Checkbox

   The input keeps being the input: appearance:none strips the native look but
   leaves a real, focusable, keyboard-operable control, which is why there is
   no hidden input and no decorative span standing in for one. The tick is a
   background image rather than a pseudo-element, because pseudo-elements on
   replaced form controls are still uneven across browsers.
   -------------------------------------------------------------------------- */

.jt-check {
    display: inline-flex;
    align-items: center;
    gap: var(--jt-3);
    /* The label is the target, so it carries the 44px minimum rather than the
       20px box the eye actually lands on. */
    min-height: 44px;
    cursor: pointer;
    font-size: 13px;
    color: var(--jt-ink);
}

.jt-check input[type='checkbox'] {
    appearance: none;
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
    margin: 0;
    /* ink-faint is 5.21:1 on the ground — comfortably past the 3:1 floor a
       control boundary has to clear to be seen at all. */
    border: 1px solid var(--jt-ink-faint);
    background: var(--jt-paper);
    cursor: pointer;
    transition: background-color var(--jt-fast) var(--jt-ease-io),
                border-color var(--jt-fast) var(--jt-ease-io);
}

.jt-check:hover input[type='checkbox'] { border-color: var(--jt-ink); }

.jt-check input[type='checkbox']:checked {
    background-color: var(--jt-ink-strong);
    border-color: var(--jt-ink-strong);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' d='M3.5 8.5l3 3 6-6'/%3E%3C/svg%3E");
    background-size: 14px 14px;
    background-position: center;
    background-repeat: no-repeat;
}

/* --------------------------------------------------------------------------
   19. The cart
   -------------------------------------------------------------------------- */

.jt-cart {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 360px;
    gap: var(--jt-8);
    align-items: start;
}

.jt-cart__welcome {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--jt-4);
    flex-wrap: wrap;
}

.jt-cart__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--jt-4);
    border-bottom: 1px solid var(--jt-hairline);
}

/* The CMS's updateCartStatus() toggles .active on .aiz-refresh while a
   selection is posting. Nothing else on the page reports that, so this is the
   only signal a customer gets that their click was received. */
.jt-busy {
    width: 16px;
    height: 16px;
    border: 1.5px solid var(--jt-hairline);
    border-top-color: var(--jt-ink);
    border-radius: 50%;
    opacity: 0;
    transition: opacity var(--jt-fast) linear;
}

.jt-busy.active {
    opacity: 1;
    animation: jt-spin 700ms linear infinite;
}

@keyframes jt-spin { to { transform: rotate(360deg); } }

/* Under reduced motion the spinner must still say "working", so it holds
   still and shows as a solid ring instead of turning. */
@media (prefers-reduced-motion: reduce) {
    .jt-busy.active { animation: none; border-color: var(--jt-ink); }
}

.jt-cart__list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.jt-line {
    display: grid;
    grid-template-columns: auto 84px minmax(0, 1fr) auto auto auto;
    align-items: center;
    gap: var(--jt-4);
    padding-block: var(--jt-5);
    border-bottom: 1px solid var(--jt-hairline);
}

.jt-line__select { min-width: 44px; justify-content: center; }

.jt-line__media {
    display: block;
    aspect-ratio: 1 / 1.2;
    background: var(--jt-ground-raised);
    overflow: hidden;
}

.jt-line__media img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    padding: 10%;
}

.jt-line__body { display: grid; gap: var(--jt-1); min-width: 0; }

.jt-line__name {
    font-family: var(--jt-display);
    font-size: 15px;
    color: var(--jt-ink-strong);
    text-decoration: none;
    transition: color var(--jt-fast) var(--jt-ease-io);
}

.jt-line__name:hover { color: var(--jt-accent); }

.jt-line__total { text-align: right; min-width: 90px; }

/* The remove control is the one destructive action on the page, so it stays
   recessive until reached for — and turns to the error colour rather than the
   accent, because the accent means "go on". */
.jt-line__remove { color: var(--jt-ink-faint); }
.jt-line__remove:hover { color: var(--jt-error); }

.jt-qty__control--sm .jt-qty__btn { width: 34px; min-height: 36px; font-size: 13px; }
.jt-qty__control--sm .jt-qty__input { width: 40px; min-height: 36px; font-size: 13px; }

.jt-cart__back { padding-top: var(--jt-5); }

/* --------------------------------------------------------------------------
   20. Order summary
   -------------------------------------------------------------------------- */

.jt-summary {
    position: sticky;
    top: 104px;
    background: var(--jt-paper);
    border: 1px solid var(--jt-hairline);
    padding: var(--jt-6);
}

.jt-summary__title { margin-bottom: var(--jt-5); }

.jt-summary__rows {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: var(--jt-3) var(--jt-4);
    margin: 0 0 var(--jt-5);
    padding-bottom: var(--jt-5);
    border-bottom: 1px solid var(--jt-hairline);
    font-size: 14px;
}

.jt-summary__rows dt { color: var(--jt-ink-muted); }

.jt-summary__rows dd {
    margin: 0;
    text-align: right;
    color: var(--jt-ink);
    font-variant-numeric: tabular-nums;
}

.jt-summary__total { display: grid; gap: var(--jt-1); }

.jt-price--lg { font-size: 20px; letter-spacing: 0.06em; }

.jt-summary__coupon { margin-top: var(--jt-6); }

.jt-summary__coupon-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: var(--jt-2);
}

.jt-summary__coupon-applied {
    display: flex;
    align-items: baseline;
    gap: var(--jt-3);
    margin: 0 0 var(--jt-3);
}

.jt-summary__go { margin-top: var(--jt-6); }

.jt-btn--small {
    min-height: 40px;
    padding-inline: var(--jt-4);
    font-size: 10px;
}

@media (max-width: 1024px) {
    .jt-cart { grid-template-columns: 1fr; gap: var(--jt-7); }
    .jt-summary { position: static; }
}

@media (max-width: 700px) {
    /* One line becomes a small card: the picture and the name lead, the
       controls sit under them. Named areas rather than source reordering, so
       the tab order still runs select, name, quantity, remove. */
    .jt-line {
        grid-template-columns: auto 72px minmax(0, 1fr);
        grid-template-areas:
            'select media body'
            '.      qty   total'
            '.      remove remove';
        gap: var(--jt-3) var(--jt-4);
    }

    .jt-line__select { grid-area: select; }
    .jt-line__media  { grid-area: media; }
    .jt-line__body   { grid-area: body; }
    .jt-line__qty    { grid-area: qty; }
    .jt-line__total  { grid-area: total; text-align: right; }
    .jt-line__remove { grid-area: remove; justify-self: end; }

    .jt-summary { padding: var(--jt-5); }
}

/* --------------------------------------------------------------------------
   21. Checkout

   Three steps, stacked and all visible. The CMS collapses them into an
   accordion with data-parent, so opening one closes the others — at checkout
   that means you cannot see the address you are shipping to while you choose
   how it ships, which is exactly when you want to.
   -------------------------------------------------------------------------- */

.jt-checkout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 360px;
    gap: var(--jt-8);
    align-items: start;
}

.jt-step {
    background: var(--jt-paper);
    border: 1px solid var(--jt-hairline);
    padding: var(--jt-6);
}

.jt-step + .jt-step { margin-top: var(--jt-5); }

.jt-step__head {
    display: flex;
    align-items: center;
    gap: var(--jt-4);
    padding-bottom: var(--jt-5);
    margin-bottom: var(--jt-5);
    border-bottom: 1px solid var(--jt-hairline);
}

.jt-step__n {
    font-family: var(--jt-display);
    font-size: 12px;
    font-weight: 500;
    letter-spacing: var(--jt-track);
    color: var(--jt-ink-muted);
    font-variant-numeric: tabular-nums;
}

.jt-step__title { margin: 0; flex: 1 1 auto; }

.jt-step__mark {
    display: inline-flex;
    align-items: center;
    gap: var(--jt-2);
    flex-shrink: 0;
}

/* The state is a word, shown or hidden by the class the wrapper in
   justintime-ui.js sets. The CMS's green icon still turns green next to it —
   it just is not the only thing that says so any more. */
.jt-step__state { color: var(--jt-ink-muted); }
.jt-step__state [data-when='done'] { display: none; }
.jt-step.is-done .jt-step__state [data-when='todo'] { display: none; }
.jt-step.is-done .jt-step__state [data-when='done'] { display: inline; }
.jt-step.is-done .jt-step__state { color: var(--jt-ink-strong); }

.jt-checkout__agree {
    padding-top: var(--jt-5);
    margin-top: var(--jt-5);
    border-top: 1px solid var(--jt-hairline);
}

/* The agreement is a sentence with links in it, so the label wraps normally
   rather than sitting on one line beside the box. */
.jt-checkout__agree .jt-check { align-items: flex-start; line-height: 1.6; }
.jt-checkout__agree .jt-check input[type='checkbox'] { margin-top: 3px; }

.jt-checkout__actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--jt-5);
    flex-wrap: wrap;
    padding-top: var(--jt-5);
}

.jt-checkout__summary { position: sticky; top: 104px; }

/* The order button is disabled until every step is satisfied, which is the
   page's clearest signal — so it has to look genuinely unavailable rather
   than merely dimmed. */
#submitOrderBtn:disabled { cursor: not-allowed; }

@media (max-width: 1024px) {
    .jt-checkout { grid-template-columns: 1fr; gap: var(--jt-7); }
    .jt-checkout__summary { position: static; }
}

@media (max-width: 560px) {
    .jt-step { padding: var(--jt-5) var(--jt-4); }
    .jt-step__head { flex-wrap: wrap; gap: var(--jt-3); }
    .jt-step__title { flex-basis: 100%; order: 3; }
    .jt-checkout__actions { flex-direction: column-reverse; align-items: stretch; }
    .jt-checkout__actions .jt-btn { width: 100%; }
    .jt-checkout__actions .jt-link { text-align: center; }
}

/* --------------------------------------------------------------------------
   22. Payment and delivery

   Radio cards. The whole label is the target, the input stays a real radio,
   and selection is shown by a border weight change as well as by colour — a
   1px hairline becoming a 2px ink rule is visible without seeing hue at all.
   -------------------------------------------------------------------------- */

.jt-pay__methods { border: 0; padding: 0; margin: 0 0 var(--jt-5); min-width: 0; }
.jt-pay__methods legend { padding: 0; }

.jt-pay__grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: var(--jt-3);
}

.jt-pay__option { display: block; cursor: pointer; }

/* The input is not hidden — hiding it takes it out of the keyboard's reach.
   It is moved behind the card it controls, so the card is what you see and
   the input is still what you focus. */
.jt-pay__option input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    margin: 0;
}

.jt-pay__face {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--jt-3);
    min-height: 64px;
    padding: var(--jt-3) var(--jt-4);
    background: var(--jt-paper);
    border: 1px solid var(--jt-hairline);
    transition: border-color var(--jt-fast) var(--jt-ease-io),
                box-shadow var(--jt-fast) var(--jt-ease-io);
}

.jt-pay__option:hover .jt-pay__face { border-color: var(--jt-ink-faint); }

/* Selected: the border darkens and doubles. box-shadow rather than a wider
   border, so nothing reflows by a pixel when the choice changes. */
.jt-pay__option input:checked + .jt-pay__face {
    border-color: var(--jt-ink-strong);
    box-shadow: inset 0 0 0 1px var(--jt-ink-strong);
}

/* The focus ring has to follow the input to the card, because the input
   itself is not where anyone is looking. */
.jt-pay__option input:focus-visible + .jt-pay__face {
    outline: 2px solid var(--jt-focus);
    outline-offset: 2px;
}

.jt-pay__name { font-size: 14px; color: var(--jt-ink); }

.jt-pay__logo {
    width: 56px;
    height: 32px;
    object-fit: contain;
    flex-shrink: 0;
}

.jt-pay__manual { margin-bottom: var(--jt-5); }
.jt-pay__manual .jt-field:last-child { margin-bottom: 0; }

.jt-pay__wallet {
    padding: var(--jt-5);
    background: var(--jt-ground-raised);
    border: 1px solid var(--jt-hairline);
    text-align: center;
}

.jt-delivery-group + .jt-delivery-group {
    margin-top: var(--jt-6);
    padding-top: var(--jt-6);
    border-top: 1px solid var(--jt-hairline);
}

.jt-delivery-group__title { margin-bottom: var(--jt-4); }

/* --------------------------------------------------------------------------
   23. Sign in, register, recover

   One panel, centred, on the ground colour. The CMS puts a full-height
   marketing photograph beside the form, pulled from a setting that is not
   configured here — so it rendered as a broken image the width of half the
   page. A boutique sign-in is a door, not a landing page.
   -------------------------------------------------------------------------- */

.jt-auth {
    min-height: 100vh;
    /* dvh where it exists: on mobile Safari 100vh is taller than the visible
       viewport, which pushed the submit button under the browser chrome. */
    min-height: 100dvh;
    display: grid;
    place-items: center;
    padding: var(--jt-6) var(--jt-gutter);
    background: var(--jt-ground);
}

.jt-auth__panel {
    width: 100%;
    max-width: 420px;
}

.jt-auth__mark {
    display: block;
    width: fit-content;
    margin: 0 auto var(--jt-6);
}

.jt-auth__mark img { height: 44px; width: auto; }

.jt-auth__card {
    background: var(--jt-paper);
    border: 1px solid var(--jt-hairline);
    padding: var(--jt-7) var(--jt-6);
}

.jt-auth__head { text-align: center; margin-bottom: var(--jt-6); }
.jt-auth__head h1 { margin-bottom: var(--jt-2); }
.jt-auth__head p { margin: 0; color: var(--jt-ink-muted); font-size: 14px; }

.jt-auth__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--jt-4);
    flex-wrap: wrap;
    margin-bottom: var(--jt-5);
}

.jt-auth__foot {
    margin: var(--jt-6) 0 0;
    text-align: center;
    font-size: 13px;
    color: var(--jt-ink-muted);
}

.jt-auth__back {
    display: block;
    margin-top: var(--jt-5);
    text-align: center;
}

/* The password field and its reveal control. */
.jt-secret { position: relative; }

/* A button, not the CMS's bare <i>. The handler binds by class and reaches the
   field with .siblings('input'), both of which still hold — but an <i> cannot
   be focused or named, so a keyboard user had no way to check what they had
   typed. The icon-font classes stay on the element itself because the handler
   swaps them there. */
.jt-secret .password-toggle {
    position: absolute;
    /* Top to bottom of the field, whatever height the field turns out to be.
       This was top:0 and a fixed 48px, which left the CMS's own rule in
       aiz-core.css half applied: that rule centres the icon with top:50% and
       transform:translateY(-50%), and only its top was overridden here. The
       transform survived, lifted the icon 24px, and put the eye above the
       password field instead of inside it - on sign-in and on both password
       fields of registration. */
    top: 0;
    bottom: 0;
    right: 0;
    transform: none;
    width: 48px;
    height: auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 0;
    background: none;
    color: var(--jt-ink-muted);
    font-size: 18px;
    cursor: pointer;
}

.jt-secret .password-toggle:hover { color: var(--jt-ink); }
.jt-secret input { padding-right: 48px; }

/* The CMS's own error markup, so a failed sign-in reads like the rest of the
   design rather than like Bootstrap. */
.jt-auth .invalid-feedback {
    display: block;
    margin-top: var(--jt-2);
    color: var(--jt-error) !important;
    background: none !important;
    border: 0 !important;
    padding: 0 !important;
    font-size: 12px;
}

.jt-auth .invalid-feedback::before { content: '— '; }
.jt-auth .is-invalid { border-color: var(--jt-error) !important; }

/* Social sign-in, when any provider is switched on. */
.jt-auth__or {
    display: flex;
    align-items: center;
    gap: var(--jt-4);
    margin: var(--jt-6) 0;
    color: var(--jt-ink-muted);
    font-size: 11px;
    letter-spacing: var(--jt-track);
    text-transform: uppercase;
}

.jt-auth__or::before,
.jt-auth__or::after {
    content: '';
    flex: 1 1 auto;
    height: 1px;
    background: var(--jt-hairline);
}

.jt-auth__social {
    display: flex;
    justify-content: center;
    gap: var(--jt-3);
    list-style: none;
    margin: 0;
    padding: 0;
}

.jt-auth__social a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    border: 1px solid var(--jt-hairline);
    color: var(--jt-ink);
    font-size: 18px;
    transition: border-color var(--jt-fast) var(--jt-ease-io),
                color var(--jt-fast) var(--jt-ease-io);
}

.jt-auth__social a:hover { border-color: var(--jt-ink); color: var(--jt-accent); }

@media (max-width: 480px) {
    .jt-auth__card { padding: var(--jt-6) var(--jt-5); }
}

/* An input with a Verify button attached — registration's contact
   confirmation, when that is switched on. */
.jt-verify {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: start;
}

/* The space before the Verify button belongs to the button, not to the grid.
   A column gap is kept even when the second track is empty, and the button
   only exists while verification is switched on - so without it the email
   field came out 8px narrower than every other field on the form. */
.jt-verify > * + * { margin-left: var(--jt-2); }

.jt-verify .jt-btn { min-height: 48px; white-space: nowrap; }

/* intl-tel-input inserts its own wrapper around the phone field, which breaks
   a grid child unless it is told to fill the track it was given. */
.jt-verify .iti { width: 100%; }

/* --------------------------------------------------------------------------
   24. Order confirmation

   The order number is the one thing a customer copies down, quotes on the
   phone and searches their inbox for, so it is the largest thing on the page
   after the heading. In the CMS it sat in the middle of the second of three
   stacked tables.
   -------------------------------------------------------------------------- */

.jt-confirm__code {
    display: inline-grid;
    gap: var(--jt-2);
    justify-items: center;
    margin: 0;
    padding: var(--jt-5) var(--jt-7);
    background: var(--jt-paper);
    border: 1px solid var(--jt-hairline);
}

.jt-confirm__code strong {
    font-family: var(--jt-display);
    font-size: 26px;
    font-weight: 400;
    letter-spacing: 0.08em;
    color: var(--jt-ink-strong);
    font-variant-numeric: tabular-nums;
}

.jt-order { margin-top: var(--jt-5); }

.jt-order__head {
    display: flex;
    flex-wrap: wrap;
    gap: var(--jt-7);
    padding-bottom: var(--jt-5);
    margin-bottom: var(--jt-5);
    border-bottom: 1px solid var(--jt-hairline);
}

@media (max-width: 560px) {
    .jt-confirm__code { padding: var(--jt-4) var(--jt-5); width: 100%; }
    .jt-confirm__code strong { font-size: 21px; }
    .jt-order__head { gap: var(--jt-4); }
}

/* --------------------------------------------------------------------------
   25. The account area

   Two columns: a quiet list of destinations and the panel they open. On a
   phone the list becomes a single horizontal row that scrolls, rather than a
   drawer behind a hamburger — there are seven entries, and hiding seven links
   behind a button to save a hundred pixels is a bad trade.
   -------------------------------------------------------------------------- */

.jt-account {
    display: grid;
    grid-template-columns: 260px minmax(0, 1fr);
    gap: var(--jt-8);
    align-items: start;
}

.jt-account__nav { position: sticky; top: 104px; }

.jt-account__who {
    display: flex;
    align-items: center;
    gap: var(--jt-3);
    padding-bottom: var(--jt-5);
    margin-bottom: var(--jt-5);
    border-bottom: 1px solid var(--jt-hairline);
}

.jt-account__avatar {
    flex-shrink: 0;
    width: 44px;
    height: 44px;
    overflow: hidden;
    border-radius: 50%;
    background: var(--jt-ground-raised);
}

.jt-account__avatar img { width: 100%; height: 100%; object-fit: cover; }

.jt-account__name {
    display: block;
    font-family: var(--jt-display);
    font-size: 15px;
    color: var(--jt-ink-strong);
}

.jt-account__who .jt-micro {
    display: block;
    /* An address longer than the column must not widen the grid track. */
    max-width: 180px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.jt-account__list {
    list-style: none;
    margin: 0 0 var(--jt-6);
    padding: 0;
}

.jt-account__link {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--jt-3);
    min-height: 44px;
    padding: var(--jt-2) 0;
    color: var(--jt-ink-muted);
    font-size: 14px;
    text-decoration: none;
    transition: color var(--jt-fast) var(--jt-ease-io);
}

.jt-account__link:hover { color: var(--jt-ink); }

/* The current page is marked by weight and a rule, not by colour alone —
   and by aria-current in the markup, which is what a screen reader reads. */
.jt-account__link.is-current {
    color: var(--jt-ink-strong);
    font-weight: 500;
    box-shadow: inset 2px 0 0 0 var(--jt-ink-strong);
    padding-left: var(--jt-4);
}

.jt-account__badge {
    flex-shrink: 0;
    padding: 2px 7px;
    background: var(--jt-accent);
    color: #fff;
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.jt-account__foot {
    padding-top: var(--jt-5);
    border-top: 1px solid var(--jt-hairline);
    display: grid;
    gap: var(--jt-4);
    justify-items: center;
}

.jt-account__foot .jt-btn { width: 100%; }
.jt-account__delete { color: var(--jt-ink-faint); }
.jt-account__delete:hover { color: var(--jt-error); }

.jt-account__panel { min-width: 0; }

@media (max-width: 1024px) {
    /* minmax(0, 1fr), not 1fr: a bare 1fr track may not shrink below its
       widest content, and the CMS's own profile and notification markup (a
       Bootstrap .row pulled 15px past its card) widened the one column past
       the screen - every signed-in page with such a row scrolled sideways. */
    .jt-account { grid-template-columns: minmax(0, 1fr); gap: var(--jt-6); }
    .jt-account__nav { min-width: 0; }
    .jt-account__nav { position: static; }

    .jt-account__list {
        display: flex;
        gap: var(--jt-5);
        overflow-x: auto;
        margin-bottom: var(--jt-5);
        padding-bottom: var(--jt-2);
        border-bottom: 1px solid var(--jt-hairline);
        /* Scrolls with the finger, snaps to a whole entry. */
        scroll-snap-type: x proximity;
    }

    .jt-account__list li { flex: 0 0 auto; scroll-snap-align: start; }

    .jt-account__link.is-current {
        padding-left: 0;
        box-shadow: inset 0 -2px 0 0 var(--jt-ink-strong);
    }

    .jt-account__foot {
        grid-template-columns: auto auto;
        justify-content: space-between;
        align-items: center;
    }

    .jt-account__foot .jt-btn { width: auto; }
}

/* --------------------------------------------------------------------------
   26. Saved pieces, orders, tabs
   -------------------------------------------------------------------------- */

.jt-saved { display: grid; }

.jt-saved__actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--jt-3);
    margin-top: var(--jt-4);
    flex-wrap: wrap;
}

/* The CMS lays the add-to-cart control over the bottom of the photograph,
   reveals it on hover and hides it below 576px — so on a phone the primary
   action of the page does not exist, and on a desktop it is invisible until
   the pointer is already on top of it. Here it is simply always there. */
.jt-saved__actions .jt-btn { flex: 1 1 auto; }
.jt-saved__remove { color: var(--jt-ink-faint); flex: 0 0 auto; }
.jt-saved__remove:hover { color: var(--jt-error); }

.jt-orders__bar {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--jt-5);
    flex-wrap: wrap;
    border-bottom: 1px solid var(--jt-hairline);
    margin-bottom: var(--jt-6);
}

.jt-tabs {
    display: flex;
    gap: var(--jt-5);
    list-style: none;
    margin: 0;
    padding: 0;
    overflow-x: auto;
}

.jt-tab {
    min-height: 44px;
    padding: 0 0 var(--jt-3);
    border: 0;
    border-bottom: 2px solid transparent;
    background: none;
    color: var(--jt-ink-muted);
    font-family: var(--jt-sans);
    font-size: 11px;
    font-weight: 500;
    letter-spacing: var(--jt-track);
    text-transform: uppercase;
    white-space: nowrap;
    cursor: pointer;
    transition: color var(--jt-fast) var(--jt-ease-io);
}

.jt-tab:hover { color: var(--jt-ink); }

/* `active` and not a class of this file's own: changeTab() sets that one. */
.jt-tab.active {
    color: var(--jt-ink-strong);
    border-bottom-color: var(--jt-ink-strong);
}

.jt-orders__filter {
    display: flex;
    align-items: center;
    gap: var(--jt-3);
    padding-bottom: var(--jt-2);
}

.jt-orders__filter label { white-space: nowrap; }

@media (max-width: 700px) {
    .jt-orders__bar { align-items: stretch; }
    .jt-orders__filter { justify-content: space-between; padding-bottom: var(--jt-3); }
}

/* The three numbers at the top of the account. Each is a link to the list it
   counts — a figure that cannot be opened is a decoration. */
.jt-tallies {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: var(--jt-4);
}

.jt-tally {
    display: grid;
    gap: var(--jt-2);
    padding: var(--jt-5);
    background: var(--jt-paper);
    border: 1px solid var(--jt-hairline);
    color: inherit;
    text-decoration: none;
    transition: border-color var(--jt-fast) var(--jt-ease-io);
}

.jt-tally:hover { border-color: var(--jt-ink); }

.jt-tally__n {
    font-family: var(--jt-display);
    font-size: 30px;
    font-weight: 300;
    line-height: 1;
    color: var(--jt-ink-strong);
    font-variant-numeric: tabular-nums;
}

/* A money figure carries a currency symbol and three or four more digits than
   a count, so it is set smaller rather than allowed to wrap. */
.jt-tally__n--sm { font-size: 20px; }

.jt-tally__label {
    font-size: 11px;
    font-weight: 500;
    letter-spacing: var(--jt-track);
    text-transform: uppercase;
    color: var(--jt-ink-muted);
}

/* --------------------------------------------------------------------------
   27. Written pages

   Privacy, terms, returns: prose that comes out of the database as HTML with
   no classes on it, so it is styled by element. The measure is set in ch
   rather than px, so it holds whatever the font size turns out to be — the
   CMS runs these to the full container width, which on a wide screen is a
   hundred and forty characters a line, and past about seventy-five the eye
   loses the start of the next one.
   -------------------------------------------------------------------------- */

.jt-crumbs {
    display: flex;
    align-items: center;
    gap: var(--jt-2);
    margin-bottom: var(--jt-6);
    font-size: 11px;
    font-weight: 500;
    letter-spacing: var(--jt-track);
    text-transform: uppercase;
    color: var(--jt-ink-muted);
}

.jt-crumbs a { color: var(--jt-ink-muted); text-decoration: none; }
.jt-crumbs a:hover { color: var(--jt-accent); }
.jt-crumbs [aria-current] { color: var(--jt-ink-strong); }

.jt-doc__head {
    /* The text column's width, not 30ch: 30ch is measured in the small body
       face, so a three-word title in the display size broke onto three lines. */
    max-width: 72ch;
    padding-bottom: var(--jt-6);
    margin-bottom: var(--jt-7);
    border-bottom: 1px solid var(--jt-hairline);
}

.jt-doc__head .jt-h1 { margin: 0; text-wrap: balance; }

.jt-doc {
    max-width: 72ch;
    font-size: 16px;
    line-height: 1.75;
    color: var(--jt-ink);
}

.jt-doc > * + * { margin-top: var(--jt-4); }

.jt-doc h1, .jt-doc h2, .jt-doc h3, .jt-doc h4 {
    font-family: var(--jt-display);
    font-weight: 400;
    color: var(--jt-ink-strong);
    line-height: 1.25;
    margin-top: var(--jt-7);
    margin-bottom: var(--jt-3);
}

.jt-doc h1 { font-size: 28px; }
.jt-doc h2 { font-size: 23px; }
.jt-doc h3 { font-size: 19px; }
.jt-doc h4 { font-size: 16px; }

/* First heading of the document sits under the page title, so it does not
   need the space above that separates it from a paragraph. */
.jt-doc > :first-child { margin-top: 0; }

.jt-doc ul, .jt-doc ol { padding-left: 1.4em; }
.jt-doc li + li { margin-top: var(--jt-2); }

.jt-doc a { color: var(--jt-accent); }
.jt-doc a:hover { color: var(--jt-accent-bright); }

.jt-doc strong, .jt-doc b { font-weight: 500; color: var(--jt-ink-strong); }

.jt-doc blockquote {
    margin: var(--jt-6) 0;
    padding-left: var(--jt-5);
    border-left: 2px solid var(--jt-hairline);
    color: var(--jt-ink-muted);
}

.jt-doc img { height: auto; }

/* Prose from a text editor arrives with tables in it. They must scroll rather
   than push the page sideways. */
.jt-doc table {
    display: block;
    overflow-x: auto;
    width: 100%;
    border-collapse: collapse;
}

.jt-doc th, .jt-doc td {
    padding: var(--jt-3) var(--jt-4);
    border-bottom: 1px solid var(--jt-hairline);
    text-align: left;
}

.jt-doc th { font-weight: 500; color: var(--jt-ink-strong); }

/* The editor emits inline colours and background colours that were chosen
   against a white page. Left alone they land as dark grey on the cream ground
   or, worse, as white on white. */
.jt-doc [style*="color"] { color: inherit !important; }
.jt-doc [style*="background"] { background: none !important; }

/* --------------------------------------------------------------------------
   28. Track an order
   -------------------------------------------------------------------------- */

.jt-track__row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: var(--jt-2);
}

@media (max-width: 480px) {
    .jt-track__row { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   29. Fact lists

   A run of short facts — a reference, a case size, a material — separated by a
   dot.

   The dot is drawn, not typed. It used to be U+00B7 MIDDLE DOT, and on at
   least one machine the font in use had no glyph for it, so all three fell
   through to tofu: three filled boxes on an otherwise empty-looking line,
   which reads as a page that failed to load rather than as a missing
   character.

   A 3px circle in currentColor has no font to depend on. It takes the colour
   of the text it sits in, and it is placed with logical properties, so it
   lands on the correct side in Arabic without a second rule.
   -------------------------------------------------------------------------- */

.jt-facts {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--jt-2);
    /* The line has to breathe: at 11px with 0.2em tracking it is dense. */
    line-height: 1.8;
}

.jt-facts > * + * {
    display: inline-flex;
    align-items: center;
    gap: var(--jt-2);
}

.jt-facts > * + *::before {
    content: '';
    flex: 0 0 auto;
    width: 3px;
    height: 3px;
    border-radius: 50%;
    background: currentColor;
    /* Held back from full strength so the facts read as the content and the
       dots as the punctuation between them. */
    opacity: 0.55;
}

/* --------------------------------------------------------------------------
   30. The rail and the drawers

   Two circular controls on the trailing edge of the product photograph, each
   opening a panel over it: the 3D model, and the rest of the collection.

   Measured off the reference: a 56px circle on a translucent white ground,
   stacked with 16px between them, vertically centred. The panel takes 42% of
   the viewport and slides in from the trailing edge.
   -------------------------------------------------------------------------- */

.jt-rail {
    position: absolute;
    /* Logical, so the rail moves to the left edge in Arabic without a second
       rule — and so the drawer it opens comes from the same side. */
    inset-inline-end: max(3vw, 16px);
    top: 50%;
    transform: translateY(-50%);
    z-index: 3;
    display: grid;
    gap: var(--jt-4);
}

/* 64px with a hairline ring and no fill, which is the reference's treatment:
   the icon is the control and the ring only says where it ends. The fill and
   the blur that were here read as browser chrome laid over the photograph. */
.jt-rail__btn {
    width: 64px;
    height: 64px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 1px solid rgba(255, 253, 251, 0.1);
    border-radius: 50%;
    background: transparent;
    color: var(--jt-on-taupe);
    cursor: pointer;
    transition: background-color var(--jt-base) var(--jt-ease-io),
                border-color var(--jt-base) var(--jt-ease-io);
}

.jt-rail__btn:hover,
.jt-rail__btn[aria-expanded='true'] {
    background: rgba(255, 253, 251, 0.1);
    border-color: rgba(255, 253, 251, 0.4);
}

/* The drawer ------------------------------------------------------------- */

.jt-drawer {
    position: absolute;
    inset-block: 0;
    inset-inline-end: 0;
    z-index: 4;
    width: min(42vw, 620px);
    display: flex;
    flex-direction: column;
    background: var(--jt-ground-raised);
    border-inline-start: 1px solid var(--jt-hairline);
}

/* Slides in when opened. A keyframe rather than a transition because the
   panel is display:none until it opens, and a transition has nothing to
   interpolate from. Under reduced motion — where data-jt-motion is absent —
   it simply appears. */
[data-jt-motion] .jt-drawer:not([hidden]) {
    animation: jt-drawer-in var(--jt-slow) var(--jt-ease) both;
}

@keyframes jt-drawer-in {
    from { opacity: 0; transform: translateX(6%); }
    to   { opacity: 1; transform: none; }
}

/* Arabic: the panel arrives from the other side, so it must travel the other
   way. transform is physical and does not follow the writing mode. */
[dir='rtl'] [data-jt-motion] .jt-drawer:not([hidden]) {
    animation-name: jt-drawer-in-rtl;
}

@keyframes jt-drawer-in-rtl {
    from { opacity: 0; transform: translateX(-6%); }
    to   { opacity: 1; transform: none; }
}

.jt-drawer__body {
    flex: 1 1 auto;
    min-height: 0;
    padding: var(--jt-6);
    display: flex;
    flex-direction: column;
    justify-content: center;
}

.jt-drawer__body--scroll {
    display: block;
    overflow-y: auto;
    overscroll-behavior: contain;
}

.jt-drawer__stage {
    position: relative;
    flex: 1 1 auto;
    min-height: 0;
    background: var(--jt-paper);
    border: 1px solid var(--jt-hairline);
}

/* The close control is a footer, not a floating button: with a scrolling
   panel above it, content slides underneath and needs an opaque edge to slide
   under. Without one the last price in the list and the X sat on top of each
   other. */
.jt-drawer__close {
    flex: 0 0 auto;
    width: 48px;
    height: 48px;
    margin: 0 auto;
    /* The band it sits in, drawn with padding rather than a wrapper so the
       markup stays one element. */
    box-shadow: 0 -1px 0 0 var(--jt-hairline);
    outline-offset: -2px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 1px solid var(--jt-hairline);
    background: var(--jt-paper);
    color: var(--jt-ink);
    cursor: pointer;
    transition: border-color var(--jt-fast) var(--jt-ease-io);
}

.jt-drawer__close:hover { border-color: var(--jt-ink); }

/* The band. Sized here rather than on the button so the button stays 48px —
   the smallest target that can be hit reliably. */
.jt-drawer { padding-bottom: 0; }
.jt-drawer > .jt-drawer__close { margin-block: var(--jt-5); }

/* The 3D stage inside the drawer ----------------------------------------- */

.jt-stage { display: grid; place-items: center; }

.jt-stage__poster {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
    padding: 8%;
    grid-area: 1 / 1;
}

.jt-stage canvas {
    grid-area: 1 / 1;
    width: 100% !important;
    height: 100% !important;
    display: block;
}

/* The hint sits over the model rather than beside it, and goes as soon as the
   customer touches the model — a permanent instruction on a photograph is an
   admission that the affordance is not obvious. */
.jt-stage__hint {
    position: absolute;
    inset-inline: 0;
    bottom: var(--jt-4);
    margin: 0;
    text-align: center;
    color: var(--jt-ink-muted);
    pointer-events: none;
    transition: opacity var(--jt-base) var(--jt-ease-io);
}

.jt-stage.is-touched .jt-stage__hint { opacity: 0; }

/* Another piece in the collection ---------------------------------------- */

.jt-alt {
    display: grid;
    justify-items: center;
    gap: var(--jt-1);
    padding: var(--jt-6) var(--jt-5);
    color: inherit;
    text-decoration: none;
    text-align: center;
    border-bottom: 1px solid var(--jt-hairline);
}

.jt-alt:last-child { border-bottom: 0; }

.jt-alt__media {
    display: block;
    width: 100%;
    aspect-ratio: 1 / 1.15;
    margin-bottom: var(--jt-3);
    background: var(--jt-paper);
    overflow: hidden;
}

.jt-alt__media img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    padding: 10%;
    transition: transform var(--jt-slow) var(--jt-ease);
}

@media (hover: hover) and (prefers-reduced-motion: no-preference) {
    .jt-alt:hover .jt-alt__media img { transform: scale(1.04); }
}

.jt-alt__name {
    font-family: var(--jt-display);
    font-size: 16px;
    color: var(--jt-ink-strong);
}

.jt-alt:hover .jt-alt__name { color: var(--jt-accent); }

@media (max-width: 900px) {
    /* On a phone the drawer is the page: 42% of a 390px viewport is 164px,
       which is not a watch, it is a stamp. */
    .jt-drawer { width: 100%; border-inline-start: 0; }

    .jt-rail {
        inset-inline-end: 50%;
        top: auto;
        bottom: var(--jt-4);
        transform: translateX(50%);
        grid-auto-flow: column;
    }

    [dir='rtl'] .jt-rail { transform: translateX(-50%); }
}

/* The poster steps aside once the model has actually painted.

   Driven by .is-live, which the viewer adds only after the first frame is on
   screen — not when the file starts downloading. So a model that fails to
   load, or a browser without WebGL, keeps the photograph, and there is never
   a moment where the stage is empty.

   Faded rather than removed: the model is framed to sit where the photograph
   was, so cross-fading reads as the same object gaining depth. visibility
   follows so the hidden poster cannot be dragged or hit-tested. */
.jt-stage__poster {
    transition: opacity var(--jt-slow) var(--jt-ease),
                visibility 0s linear var(--jt-slow);
}

.jt-stage.is-live .jt-stage__poster {
    opacity: 0;
    visibility: hidden;
}

/* The drag hint only makes sense once there is something to drag. */
.jt-stage__hint { opacity: 0; }
.jt-stage.is-live .jt-stage__hint { opacity: 1; }
.jt-stage.is-live.is-touched .jt-stage__hint { opacity: 0; }

/* The rail steps aside for the drawer rather than being buried by it.

   Found by trying to open the second drawer while the first was open: the
   panel is 42vw wide and sits above the rail, so the other control could not
   be reached at all. The reference keeps both buttons visible immediately
   beside the open panel, which is also the only arrangement where switching
   between the two is one click rather than close-then-open.

   The rail is raised above the drawer as well as moved, because a control the
   panel can cover is a control that will be covered again the next time
   somebody changes a width. */
.jt-rail { transition: inset-inline-end var(--jt-slow) var(--jt-ease); }

/* Open, the rail sits astride the panel's leading edge, halfway down it.

   An earlier version stood the pair in the panel's bottom corner, side by
   side, to keep them off the strap thumbnails and off the model. It kept them
   clear of both and read as two loose circles parked in a corner - which is
   what they looked like.

   On the edge they are neither on the photograph nor on the panel but on the
   seam between them, which is what the buttons are for: the panel is what
   they open and close. Halfway down clears the thumbnails, which occupy the
   top of that column, and the model, which is centred in the room left over. */
.jt-drawer-open .jt-rail {
    z-index: 5;

    /* Centre of the button on the panel's leading edge: the panel starts
       min(42vw, 620px) in from the trailing edge, and half a 64px button is
       32px. */
    inset-inline-end: calc(min(42vw, 620px) - 32px);
    top: 50%;
    bottom: auto;
    transform: translateY(-50%);
    grid-auto-flow: row;
}

/* Straddling the seam, half of each button is over cream and half over the
   photograph, so neither the translucent-on-dark treatment nor a bare ring
   can work - whichever ground you tune for, the other half disappears.

   Solid, with its own shadow. It reads on both, and the shadow is what makes
   it sit on the seam rather than in a hole cut through it. */
.jt-drawer-open .jt-rail__btn {
    border-color: rgba(24, 24, 24, 0.08);
    background: var(--jt-paper);
    color: var(--jt-ink);
    box-shadow: 0 2px 14px rgba(24, 24, 24, 0.16);
}

.jt-drawer-open .jt-rail__btn:hover,
.jt-drawer-open .jt-rail__btn[aria-expanded='true'] {
    border-color: rgba(24, 24, 24, 0.2);
    background: var(--jt-ground-raised);
    box-shadow: 0 4px 18px rgba(24, 24, 24, 0.22);
}

@media (max-width: 900px) {
    /* The drawer is the whole width here, so there is nowhere beside it to
       stand. The rail goes under the panel instead of beside it. */
    .jt-drawer-open .jt-rail { display: none; }
}

/* --------------------------------------------------------------------------
   31. Straps

   A column of thumbnails at the top-left of the stage, one per strap. Clicking
   one swaps the model in place — it is the same watch, so navigating away
   would be a lie.
   -------------------------------------------------------------------------- */

.jt-straps {
    position: absolute;
    top: var(--jt-5);
    inset-inline-start: var(--jt-5);
    z-index: 2;
    display: grid;
    gap: var(--jt-2);
}

.jt-strap {
    width: 56px;
    height: 68px;
    padding: 4px;
    border: 1px solid transparent;
    background: none;
    cursor: pointer;
    opacity: 0.55;
    transition: opacity var(--jt-fast) var(--jt-ease-io),
                border-color var(--jt-fast) var(--jt-ease-io);
}

.jt-strap img { width: 100%; height: 100%; object-fit: contain; }

.jt-strap:hover { opacity: 1; }

/* The chosen strap is marked by full opacity and a rule down its leading edge,
   not by colour — and by aria-pressed, which is what a screen reader reads. */
.jt-strap.is-active {
    opacity: 1;
    border-inline-start: 2px solid var(--jt-ink-strong);
}

/* While a strap's model is loading, the stage says so rather than sitting
   still and looking broken. */
.jt-stage.is-swapping .jt-stage__hint::after {
    content: '';
    display: inline-block;
    width: 10px;
    height: 10px;
    margin-inline-start: var(--jt-2);
    border: 1.5px solid var(--jt-hairline);
    border-top-color: var(--jt-ink);
    border-radius: 50%;
    vertical-align: -1px;
    animation: jt-spin 700ms linear infinite;
}

.jt-stage.is-swapping .jt-stage__hint { opacity: 1; }

@media (max-width: 900px) {
    .jt-straps {
        top: auto;
        bottom: var(--jt-4);
        inset-inline-start: 50%;
        transform: translateX(-50%);
        grid-auto-flow: column;
    }

    [dir='rtl'] .jt-straps { transform: translateX(50%); }
}

/* The finishes panel names what it is holding. Without a title the panel is
   a column of watches with no explanation of why these four. */
.jt-drawer__title {
    margin: 0 0 var(--jt-4);
    padding-bottom: var(--jt-4);
    border-bottom: 1px solid var(--jt-hairline);
    color: var(--jt-ink-muted);
}

/* The user guide link. The mark sits on the text baseline rather than beside
   it, so a wrapped label keeps its icon attached to the first line. */
.jt-guide {
    display: inline-flex;
    align-items: center;
    gap: var(--jt-2);
}

.jt-guide svg { flex-shrink: 0; }

/* --------------------------------------------------------------------------
   32. The editorial run

   Everything between the hero and the related grid: the introduction, the
   gallery, the banner, the specification and the straps.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   The gallery

   Two photographs at a time, edge to edge, advancing two at a time. There is
   no gutter between them and no padding around them: the pair is one
   composition, and a margin would turn it into two pictures of a watch.

   A note on the images themselves. The catalogue as imported is cut-out
   product shots — median 320x529 — which cannot fill a frame this size
   without being enlarged past recognition. object-fit is `contain` while that
   is true, so nothing is blown up; the moment real photography is uploaded,
   switching the one declaration below to `cover` gives the reference's
   full-bleed spread with no other change.
   -------------------------------------------------------------------------- */

.jt-gallery { display: block; }

.jt-gallery__viewport {
    position: relative;
    background: var(--jt-ground-raised);
}

.jt-gallery__pair {
    display: grid;
    grid-template-columns: 1fr 1fr;
}

.jt-gallery__frame {
    margin: 0;
    display: grid;
    place-items: center;
    /* Tall enough to read as a spread, capped so it never eats a whole
       laptop screen. */
    height: min(72vh, 660px);
    overflow: hidden;
}

.jt-gallery__frame:nth-child(2) { background: var(--jt-paper); }

/* width:auto with a cap, not width:100% with object-fit.
   object-fit: contain still scales an image UP to fill its box — it only
   promises not to crop. A 497x768 product shot in an 795px frame came out at
   795x1176, which is the same 1.6x enlargement this page was supposed to have
   stopped doing.
   auto sizing under a cap does what was actually wanted: a large photograph
   fills the frame, a small cut-out sits at its own size in the middle of it,
   and neither is ever enlarged. */
.jt-gallery__frame img {
    width: auto;
    height: auto;
    max-width: 88%;
    max-height: 88%;
    object-fit: contain;
}

/* Navigation.

   Not a pair of arrows pinned to the edges. The left and right halves of the
   photograph are themselves the controls, and a circle follows the cursor
   across them saying which one it is over — which is the reference's own
   behaviour, and a better one: the target is half the image rather than an
   84px disc somebody has to aim at.

   The buttons carry no appearance of their own. They are the hit area and the
   accessible name; the circle is what is seen.
   -------------------------------------------------------------------------- */

.jt-gallery__half {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 50%;
    z-index: 2;
    padding: 0;
    border: 0;
    background: none;
    /* A pointer with no arrow of its own — the circle is the affordance, and a
       second one under it would be two things saying the same thing. */
    cursor: pointer;
}

.jt-gallery__half--prev { inset-inline-start: 0; }
.jt-gallery__half--next { inset-inline-end: 0; }

/* A keyboard has no cursor to follow, so focus draws the outline on the half
   itself. Inset, because an outline on an element flush with the viewport
   edge would otherwise be clipped away. */
.jt-gallery__half:focus-visible { outline: 2px solid var(--jt-focus); outline-offset: -6px; }

.jt-gallery__cursor {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 3;
    display: grid;
    place-items: center;
    width: 96px;
    height: 96px;
    margin: -48px 0 0 -48px;   /* centred on the pointer */
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.9);
    color: var(--jt-ink);
    text-align: center;
    /* It must never eat the click meant for the half underneath it. */
    pointer-events: none;
    opacity: 0;
    /* scale and translate as independent properties, not as one transform.
       The script writes the position; if both wrote `transform` the last one
       to run would erase the other, and the circle would either never grow or
       never move. */
    scale: 0.8;
    transition: opacity var(--jt-fast) var(--jt-ease-io),
                scale var(--jt-fast) var(--jt-ease-io);
}

.jt-gallery__cursor.is-visible { opacity: 1; scale: 1; }

/* Under reduced motion the circle still appears and still follows — it is an
   affordance, not decoration — but it does not grow into place. */
@media (prefers-reduced-motion: reduce) {
    .jt-gallery__cursor { transition: opacity var(--jt-fast) linear; scale: 1; }
}

/* Touch has no pointer to follow. The circle would sit wherever the last tap
   landed and never move again, so it does not appear at all — a tap on either
   half still works, which is what a touch user expects from a gallery. */
@media (hover: none) {
    .jt-gallery__cursor { display: none; }
}

/* The strip. Centred under the pair, one thumbnail per photograph — clicking
   any of them jumps to the pair it belongs to. */
.jt-gallery__strip {
    display: flex;
    justify-content: center;
    gap: var(--jt-1);
    padding: var(--jt-3) var(--jt-4);
    background: var(--jt-ground-raised);
    overflow-x: auto;
}

.jt-gallery__thumb {
    flex: 0 0 auto;
    width: 56px;
    height: 56px;
    padding: 3px;
    border: 1px solid transparent;
    background: var(--jt-paper);
    cursor: pointer;
    opacity: 0.5;
    transition: opacity var(--jt-fast) var(--jt-ease-io),
                border-color var(--jt-fast) var(--jt-ease-io);
}

.jt-gallery__thumb img { width: 100%; height: 100%; object-fit: contain; }

.jt-gallery__thumb:hover { opacity: 1; }

/* The pair on screen is marked by opacity and a border, not by colour. */
.jt-gallery__thumb.is-current {
    opacity: 1;
    border-color: var(--jt-ink-strong);
}

@media (max-width: 700px) {
    /* One at a time on a phone: half of a 390px viewport is not a
       photograph, it is a stamp. */
    .jt-gallery__pair { grid-template-columns: 1fr; }
    .jt-gallery__frame:nth-child(2) { display: none; }
    .jt-gallery__frame { height: 60vh; }
    .jt-gallery__nav { width: 56px; height: 56px; }
}

/* The banner ------------------------------------------------------------- */

/* Not a photographic banner, because there is no photograph to put in one.
   A composition instead: the watch at its own size on the ground it is shot
   against, with the line beside it rather than laid over it. Type over a
   cut-out needs a scrim to be readable at all, and a scrim over an image that
   is mostly empty background is a grey box with a watch in it. */
.jt-banner {
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    align-items: center;
    gap: var(--jt-8);
    /* The ground runs edge to edge; the content does not. Without the inline
       padding the copy started at the browser frame while every other section
       on the page started at 1360px centred, and the banner read as a
       different website. */
    padding: var(--jt-9) max(var(--jt-gutter), calc((100% - var(--jt-max)) / 2));
    background: var(--jt-taupe);
    color: var(--jt-on-taupe);
}

.jt-banner__copy { text-align: start; }

.jt-banner__copy .jt-eyebrow { color: var(--jt-on-taupe); opacity: 0.75; }

.jt-banner__line {
    margin: var(--jt-3) 0 var(--jt-6);
    color: var(--jt-on-taupe);
}

.jt-banner__cta {
    display: inline-block;
    padding-bottom: var(--jt-1);
    border-bottom: 1px solid currentColor;
    color: var(--jt-on-taupe);
    text-decoration: none;
}

.jt-banner__image {
    justify-self: center;
    max-width: min(420px, 100%);
    max-height: 460px;
    width: auto;
    height: auto;
    object-fit: contain;
}

/* Specification ---------------------------------------------------------- */

.jt-specs {
    display: grid;
    grid-template-columns: 200px minmax(0, 1fr);
    gap: var(--jt-8);
    align-items: start;
    margin-top: var(--jt-8);
}

.jt-specs__tabs {
    display: grid;
    gap: var(--jt-2);
    position: sticky;
    top: 120px;
}

.jt-specs__tab {
    justify-self: start;
    min-height: 44px;
    padding: 0;
    border: 0;
    background: none;
    color: var(--jt-ink-muted);
    font-family: var(--jt-display);
    font-size: 17px;
    text-align: start;
    cursor: pointer;
    transition: color var(--jt-fast) var(--jt-ease-io);
}

.jt-specs__tab:hover { color: var(--jt-ink); }

/* The selected tab is marked by weight, colour and a leading mark — never by
   colour alone. aria-selected carries it for anyone not looking. */
.jt-specs__tab.is-current { color: var(--jt-ink-strong); }

.jt-specs__tab.is-current::before {
    content: '';
    display: inline-block;
    width: 0;
    height: 0;
    margin-inline-end: var(--jt-2);
    border-block: 4px solid transparent;
    border-inline-start: 5px solid currentColor;
    vertical-align: 2px;
}

.jt-specs__list {
    display: grid;
    grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
    gap: 0;
    margin: 0;
}

.jt-specs__list dt,
.jt-specs__list dd {
    padding: var(--jt-4) 0;
    border-bottom: 1px solid var(--jt-hairline);
    margin: 0;
}

.jt-specs__list dt {
    font-size: 11px;
    letter-spacing: var(--jt-track);
    text-transform: uppercase;
    color: var(--jt-ink-muted);
    padding-inline-end: var(--jt-5);
}

.jt-specs__list dd { color: var(--jt-ink); font-size: 15px; }

/* Straps ----------------------------------------------------------------- */

.jt-strap-card { margin: 0; text-align: center; }

.jt-strap-card__media {
    display: block;
    aspect-ratio: 1 / 1.2;
    margin-bottom: var(--jt-4);
    background: var(--jt-paper);
    overflow: hidden;
}

.jt-strap-card__media img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    padding: 12%;
}

.jt-strap-card figcaption {
    font-size: 14px;
    color: var(--jt-ink-strong);
}

@media (max-width: 900px) {
    .jt-gallery__pair { grid-template-columns: 1fr; }
    .jt-specs { grid-template-columns: 1fr; gap: var(--jt-5); }

    .jt-specs__tabs {
        position: static;
        grid-auto-flow: column;
        justify-content: start;
        gap: var(--jt-5);
        padding-bottom: var(--jt-3);
        border-bottom: 1px solid var(--jt-hairline);
    }

    .jt-specs__list { grid-template-columns: 1fr; }
    .jt-specs__list dt { padding-bottom: 0; border-bottom: 0; }
    .jt-banner__copy { padding: var(--jt-7) var(--jt-gutter); }
}

/* The straps heading is a sentence, not a label, so it gets a sentence's
   measure. Centred inside jt-listing-head it was breaking across three lines
   at 40ch. */
.jt-band .jt-listing-head { max-width: 46ch; }

@media (max-width: 900px) {
    .jt-banner {
        grid-template-columns: 1fr;
        gap: var(--jt-6);
        padding: var(--jt-8) var(--jt-gutter);
        text-align: center;
    }

    .jt-banner__copy { text-align: center; }
}

/* --------------------------------------------------------------------------
   33. The makers and the collections

   Two index pages that were still wearing the marketplace's own markup: black
   Bootstrap type on the shop's cream ground, in a typeface nothing else here
   uses. Both are now the theme's, and both follow the same rule as every
   other image on this site - a logo is never drawn larger than it was
   supplied. object-fit: contain does not promise that; it only promises not
   to crop. Capping with max-width and max-height on an auto-sized box does.
   -------------------------------------------------------------------------- */

/* auto-fit, not auto-fill.

   auto-fill keeps the empty columns it could have made, so a boutique with
   two makers got two cells and five columns of bare hairline grid stretching
   off to the right - the grid announcing how many makers the shop does not
   have. auto-fit collapses the empty tracks and lets the real cells take the
   room. */
.jt-makers {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 1px;
    background: var(--jt-hairline);
    border: 1px solid var(--jt-hairline);
}

/* The hairline grid is the gap showing through, so each cell paints its own
   ground. One border per line rather than one per cell, which is what left
   the CMS version with doubled rules down the middle. */
.jt-maker {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: space-between;
    gap: var(--jt-5);
    min-height: 460px;
    padding: var(--jt-7) var(--jt-5) var(--jt-6);
    overflow: hidden;
    background: var(--jt-ground);
    color: inherit;
    text-align: center;
    text-decoration: none;
    isolation: isolate;
    transition: background var(--jt-base) var(--jt-ease);
}

/* A soft light rising from the foot of the card, warmer on hover. */
.jt-maker::before {
    content: '';
    position: absolute;
    inset: auto -20% -40% -20%;
    height: 70%;
    z-index: -1;
    background: radial-gradient(closest-side, rgba(255, 250, 240, 0.95), rgba(255, 250, 240, 0) 100%);
    opacity: 0.6;
    transition: opacity var(--jt-slow) var(--jt-ease), transform var(--jt-slow) var(--jt-ease);
}

@media (hover: hover) {
    .jt-maker:hover { background: var(--jt-ground-raised); }
    .jt-maker:hover::before { opacity: 1; transform: translateY(-8%); }
}

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

/* The logo, large: it is the reason the card exists. */
.jt-maker__mark {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 96px;
}

.jt-maker__mark img {
    width: auto;
    height: auto;
    max-width: min(300px, 80%);
    max-height: 96px;
    transition: transform var(--jt-slow) var(--jt-ease);
}

.jt-maker__wordmark {
    font-family: var(--jt-display);
    font-size: 40px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--jt-ink-strong);
}

/* Three of the maker's watches, fanned: the middle one forward, the outer
   two turned slightly away and set back. */
.jt-maker__pieces {
    position: relative;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    width: 100%;
    height: 210px;
}

.jt-maker__piece {
    position: relative;
    display: block;
    width: 30%;
    max-width: 150px;
    height: 100%;
    margin: 0 -3%;
    transition: transform var(--jt-slow) var(--jt-ease);
}

.jt-maker__piece img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    filter: drop-shadow(0 0 18px color-mix(in srgb, var(--jt-glow, #e8c89a) 55%, transparent))
            drop-shadow(0 16px 14px rgba(40, 28, 16, 0.16));
}

.jt-maker__piece:nth-child(1) { transform: translateY(14px) rotate(-8deg) scale(0.86); z-index: 1; }
.jt-maker__piece:nth-child(2) { z-index: 2; }
.jt-maker__piece:nth-child(3) { transform: translateY(14px) rotate(8deg) scale(0.86); z-index: 1; }

@media (hover: hover) and (prefers-reduced-motion: no-preference) {
    .jt-maker:hover .jt-maker__mark img { transform: scale(1.04); }
    .jt-maker:hover .jt-maker__piece:nth-child(1) { transform: translate(-10px, 6px) rotate(-12deg) scale(0.9); }
    .jt-maker:hover .jt-maker__piece:nth-child(2) { transform: translateY(-12px) scale(1.05); }
    .jt-maker:hover .jt-maker__piece:nth-child(3) { transform: translate(10px, 6px) rotate(12deg) scale(0.9); }
}

.jt-maker__foot {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--jt-1);
}

.jt-maker__name {
    font-family: var(--jt-display);
    font-size: 22px;
    font-weight: 400;
    color: var(--jt-ink-strong);
}

.jt-maker__cta {
    margin-top: var(--jt-3);
    padding-bottom: 2px;
    color: var(--jt-ink);
    border-bottom: 1px solid currentColor;
    transition: color var(--jt-fast) var(--jt-ease);
}

.jt-maker:hover .jt-maker__cta { color: var(--jt-accent); }

/* The children of a collection, under its tile. A list of links rather than
   tiles of their own: a sub-collection is a refinement, not a destination of
   equal weight, and giving it the same tile said otherwise. */
.jt-sublist {
    display: flex;
    flex-wrap: wrap;
    gap: var(--jt-2) var(--jt-4);
    margin: var(--jt-4) 0 0;
    padding: 0;
    list-style: none;
}

.jt-sublist a {
    font-size: 12px;
    font-weight: 500;
    letter-spacing: var(--jt-track);
    text-transform: uppercase;
    color: var(--jt-ink-muted);
    text-decoration: none;
    border-bottom: 1px solid transparent;
    transition: color var(--jt-fast) var(--jt-ease),
                border-color var(--jt-fast) var(--jt-ease);
}

.jt-sublist a:hover {
    color: var(--jt-accent);
    border-bottom-color: var(--jt-accent);
}

@media (max-width: 600px) {
    .jt-makers { grid-template-columns: 1fr; }
    .jt-maker { min-height: 0; padding: var(--jt-6) var(--jt-4) var(--jt-5); }
    .jt-maker__mark { height: 72px; }
    .jt-maker__mark img { max-height: 72px; max-width: 200px; }
    .jt-maker__pieces { height: 170px; }
}

/* --------------------------------------------------------------------------
   34. The journal

   An article index and an article. Same rule as everywhere else on this site:
   the image is capped at its natural size, and the text gets a measure rather
   than whatever width is left beside a sidebar.
   -------------------------------------------------------------------------- */

.jt-entry {
    display: block;
    color: inherit;
    text-decoration: none;
}

.jt-entry:focus-visible {
    outline: 2px solid var(--jt-focus);
    outline-offset: 4px;
}

.jt-entry__media {
    display: block;
    overflow: hidden;
    background: var(--jt-ground-raised);
    aspect-ratio: 3 / 2;
    margin-bottom: var(--jt-4);
}

.jt-entry__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform var(--jt-slow) var(--jt-ease);
}

@media (hover: hover) and (prefers-reduced-motion: no-preference) {
    .jt-entry:hover .jt-entry__media img { transform: scale(1.03); }
}

.jt-entry__body {
    display: flex;
    flex-direction: column;
    gap: var(--jt-2);
}

.jt-entry__title {
    display: block;
    transition: color var(--jt-fast) var(--jt-ease);
}

@media (hover: hover) {
    .jt-entry:hover .jt-entry__title { color: var(--jt-accent); }
}

/* The most recent article: image on one side, the standfirst on the other,
   so the index opens with something to read rather than six equal tiles. */
.jt-entry--lead {
    display: grid;
    grid-template-columns: 1.2fr 1fr;
    gap: var(--jt-7);
    align-items: center;
}

.jt-entry--lead .jt-entry__media {
    aspect-ratio: 4 / 3;
    margin-bottom: 0;
}

.jt-entry--lead .jt-entry__body { gap: var(--jt-4); }
.jt-entry__standfirst { max-width: 46ch; }

/* On an article page the banner runs the width of the measure, not the page:
   a full-bleed photograph above a 70ch column reads as two documents. */
.jt-entry__media--wide {
    aspect-ratio: 21 / 9;
    max-width: 100%;
}

@media (max-width: 900px) {
    .jt-entry--lead {
        grid-template-columns: 1fr;
        gap: var(--jt-5);
    }

    .jt-entry--lead .jt-entry__media { aspect-ratio: 3 / 2; }
    .jt-entry__media--wide { aspect-ratio: 3 / 2; }
}

/* --------------------------------------------------------------------------
   Touch targets.

   Measured on a phone at 390px, every page carried fifteen to eighteen links
   too small to tap reliably: the footer's lists at 17px tall, the
   Privacy / Terms / Returns row at 20px, and the small uppercase text links
   ("See all", "Download user guide") at 15-16px. WCAG 2.2 sets 24px as the
   floor for a pointer target; a thumb wants more.

   Scoped to coarse pointers, so a mouse sees the design exactly as it was and
   only a finger gets the extra room.
   -------------------------------------------------------------------------- */
@media (pointer: coarse) {

    /* The footer lists get real height rather than padding pulled back with a
       negative margin. Those links sit 8px apart, so padding alone would make
       each tap area overlap its neighbour's - trading "too small to hit" for
       "hits the one above", which is worse. */
    .jt-footer li a,
    .jt-footer__base a {
        display: inline-flex;
        align-items: center;
        min-height: 36px;
    }

    .jt-footer li + li { margin-top: 0; }

    /* The small text links sit alone on their line, so here the hit area can
       grow without moving anything: padding out, the same margin back in. The
       underline is drawn from the bottom of the box, so it is lifted by the
       same amount to stay under the words. */
    .jt-link.jt-micro {
        display: inline-block;
        padding-block: 9px;
        margin-block: -9px;
    }

    .jt-link.jt-micro::after { bottom: 9px; }
}

/* The second pass: what was still under 24px once the footer was fixed.

   The breadcrumb's "Home", the blog's title links, and the "Register now" and
   "Sign in" links under the account forms. Padding out with the same margin
   back in, so nothing on screen moves - the blog in particular keeps exactly
   the look it had, and only the area a finger can land on grows. On an inline
   link the vertical padding does not touch the line box, so a link inside a
   sentence does not push its line apart either. */
@media (pointer: coarse) {
    .jt-crumbs a,
    .jt-auth__foot a,
    a.text-reset.hov-text-primary,
    a.btn-link {
        padding-block: 6px;
        margin-block: -6px;
    }

    .jt-crumbs a { display: inline-block; }
}

/* "Back to previous page" under the sign-in form: a lone flex link 20px tall.
   It is the way out of the page for someone who opened it by mistake, so it
   gets a full thumb's height rather than the 24px minimum. */
@media (pointer: coarse) {
    a.ml-auto.fs-14.fw-700.d-flex { min-height: 44px; }
}

/* ---------------------------------------------------------------------------
   Phone bottom bar (.aiz-mobile-bottom-nav, from the CMS footer)

   The theme's reset makes every svg a block, and a block ignores the link's
   text-align: each icon sat against the left edge of its cell while its label
   stayed centred beneath the next one over. Centre the icons again, and dress
   the bar in the boutique's ground, ink and hairline rather than the CMS's
   translucent white and grey.
   --------------------------------------------------------------------------- */
.aiz-mobile-bottom-nav {
    background-color: var(--jt-ground-raised) !important;
    border-color: var(--jt-hairline) !important;
    padding-bottom: env(safe-area-inset-bottom);
    z-index: 1030;
}
.aiz-mobile-bottom-nav a {
    color: var(--jt-ink-muted) !important;
    font-family: var(--jt-sans);
}
.aiz-mobile-bottom-nav svg { margin-inline: auto; }
.aiz-mobile-bottom-nav svg * { fill: var(--jt-ink-muted); }
.aiz-mobile-bottom-nav .svg-active svg * { fill: var(--jt-accent) !important; }
.aiz-mobile-bottom-nav span.d-block {
    font-size: 11px;
    font-weight: 400;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}
.aiz-mobile-bottom-nav .svg-active span.d-block { color: var(--jt-accent) !important; }
.aiz-mobile-bottom-nav img.rounded-circle { margin-inline: auto; }

/* The bar is fixed over the last 62px of every page below 1200px, so the
   footer's final line - copyright and the legal links - would sit under it. */
@media (max-width: 1199.98px) {
    body { padding-bottom: calc(62px + env(safe-area-inset-bottom)); }
}

/* The size-guide drawer (#rightOffcanvas) waits just past the right edge of
   the screen, and its 40px shadow does not wait with it: on a phone it paints
   a grey band down the right side of every page. No shadow until it opens. */
#rightOffcanvas { transition: right 0.4s ease, box-shadow 0s linear 0s, visibility 0s linear 0s; }
#rightOffcanvas:not(.active) {
    box-shadow: none !important;
    visibility: hidden;
    /* hold both until it has finished sliding out */
    transition: right 0.4s ease, box-shadow 0s linear 0.4s, visibility 0s linear 0.4s;
}

/* Pagination on a phone. Nine page numbers at 44px are 430px - wider than the
   screen - and the row did not wrap, so the first and last numbers were cut
   off at the screen edges. The numbers take a row of their own and wrap;
   Previous and Next share the row below, at either end, where a thumb finds
   them. Targets stay 44px. */
@media (max-width: 560px) {
    .jt-pagination { justify-content: space-between; row-gap: var(--jt-3); }
    .jt-pagination__numbers {
        order: -1;
        flex: 0 0 100%;
        flex-wrap: wrap;
        justify-content: center;
        gap: 0;
    }
}

/* CMS pages (the journal, account pages) put .row.gutters-16 - a -16px pull -
   inside Bootstrap's .container, which pads by 15px. The row came out a pixel
   past each edge and the whole page could be nudged sideways by a thumb. One
   more pixel of container padding on phones; nothing visibly moves. */
@media (max-width: 575.98px) {
    .container { padding-left: 16px; padding-right: 16px; }
}

/* Phone header: the logo in the middle of the screen.
   The bar is "menu | logo | tools", 1fr auto 1fr. On a phone the left side
   holds one icon and the right side three (four once signed in, with the
   wishlist), and the grid centres the logo in what is left over rather than
   on the screen - signed in, it sat 60px left of centre. Search moves to the
   left beside the menu, and the tools join the bar's own grid so each icon is
   a column: two either side of the logo as a guest, and one off balance, not
   three, when signed in. The DOM order, and so the tab order, is unchanged. */
@media (max-width: 560px) {
    /* Two fixed 44px tracks either side of the logo, so the sides match
       whatever the right holds: the account alone (a guest, with ordering
       off), account and wishlist (signed in), or those plus the bag - a
       third tool spills into an implicit track and costs 22px of centring,
       nothing more. */
    .jt-header__bar {
        grid-template-columns: 44px 44px minmax(0, 1fr) 44px 44px;
        grid-auto-columns: 44px;
        column-gap: 0;
    }
    .jt-header__bar > .jt-nav { grid-column: 1; grid-row: 1; }
    .jt-header__bar #jt-search-toggle { grid-column: 2; grid-row: 1; }
    .jt-header__bar > .jt-mark { grid-column: 3; grid-row: 1; justify-self: center; }
    .jt-header__bar > .jt-tools { display: contents; }
}

/* "Ask on WhatsApp" - the product page's one call to action while the shop
   takes enquiries instead of orders. WhatsApp's own dark green, so it reads
   as WhatsApp before the label is read; white on #075E54 is 8.9:1, where the
   familiar bright #25D366 would give white text under 2:1. */
.jt-btn--whatsapp {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--jt-2);
    background: #075E54;
    border-color: #075E54;
    color: #fff;
}
.jt-btn--whatsapp:hover,
.jt-btn--whatsapp:focus-visible {
    background: #054C44;
    border-color: #054C44;
    color: #fff;
}
.jt-btn--whatsapp svg { flex: 0 0 auto; }

/* The thumbnail strip, once a watch has more frames than fit across.
   centre alone pushes the first thumbnails to a negative offset inside a
   scroll box, where no scrolling can reach them; "safe" centres only while
   everything fits and aligns to the start once it does not.

   Each thumbnail is also the containing block of its screen-reader label.
   Without that, the labels (position: absolute) were placed against the page
   rather than the scroll box, escaped its clipping, and a watch with 17 frames
   made the phone lay the whole page out 1004px wide. */
.jt-gallery__thumb { position: relative; }
.jt-gallery__strip {
    justify-content: safe center;
    max-width: 100%;
    overscroll-behavior-x: contain;
}

/* ==========================================================================
   The menu (#jt-menu, in header1)

   A panel from the inline-start edge over a scrim. It replaces the CMS's
   sidebar, which the homepage never had - the burger there did nothing.
   Above the mobile bottom bar, the sticky header and the CMS's cookie notice.
   ========================================================================== */
html.jt-menu-open { overflow: hidden; }

/* GetButton's floating WhatsApp bubble stacks at 16 million; the CMS layout
   already targets its generated class, so hide it while the menu is open -
   the menu carries its own WhatsApp button. */
html.jt-menu-open .sc-q8c6tt-3 { visibility: hidden !important; }

.jt-menu {
    position: fixed;
    inset: 0;
    z-index: 10000; /* the CMS's alert stack sits at 9999 */
}

.jt-menu__scrim {
    position: absolute;
    inset: 0;
    background: rgba(24, 24, 24, 0.35);
}

.jt-menu__panel {
    position: absolute;
    inset-block: 0;
    inset-inline-start: 0;
    width: min(88vw, 420px);
    display: flex;
    flex-direction: column;
    background: var(--jt-ground);
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: max(var(--jt-3), env(safe-area-inset-top)) var(--jt-5) max(var(--jt-5), env(safe-area-inset-bottom));
    box-shadow: 0 0 40px rgba(24, 24, 24, 0.12);
}

.jt-menu__top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    min-height: 56px;
    margin-inline-end: calc(var(--jt-3) * -1);
}

.jt-menu__mark img { display: block; height: 42px; width: auto; }
.jt-menu__mark span { font-family: var(--jt-display); font-size: 18px; color: var(--jt-ink-strong); }

.jt-menu__search {
    display: flex;
    align-items: center;
    margin-block: var(--jt-4) var(--jt-3);
    border-bottom: 1px solid var(--jt-ink);
}

/* !important only to beat justintime-cms.css, which boxes every search input. */
.jt-menu .jt-menu__search-input {
    flex: 1;
    min-width: 0;
    height: 44px;
    min-height: 44px;
    padding: 0;
    border: 0 !important;
    border-radius: 0;
    box-shadow: none;
    -webkit-appearance: none;
    appearance: none;
    background: transparent !important;
    font-family: var(--jt-sans);
    font-size: 16px; /* 16px keeps iOS from zooming on focus */
    color: var(--jt-ink-strong);
}

.jt-menu__search-input:focus { outline: none; }
.jt-menu__search:focus-within { border-bottom-color: var(--jt-accent); }
.jt-menu__search-input::placeholder { color: var(--jt-ink-muted); }

.jt-menu__nav ul { list-style: none; margin: 0; padding: 0; }

.jt-menu__link {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--jt-3);
    min-height: 56px;
    border-bottom: 1px solid var(--jt-hairline);
    font-family: var(--jt-display);
    font-size: 22px;
    line-height: 1.2;
    color: var(--jt-ink-strong);
    text-decoration: none;
}

.jt-menu__link svg { flex: none; color: var(--jt-ink-muted); transition: transform var(--jt-fast) var(--jt-ease-io); }
.jt-menu__link:hover,
.jt-menu__link[aria-current='page'] { color: var(--jt-accent); }
.jt-menu__link:hover svg { transform: translateX(3px); color: var(--jt-accent); }

.jt-menu__secondary { margin-top: var(--jt-5) !important; }

.jt-menu__secondary a {
    display: flex;
    align-items: center;
    min-height: 44px;
    font-family: var(--jt-sans);
    font-size: 12px;
    letter-spacing: var(--jt-track);
    text-transform: uppercase;
    color: var(--jt-ink);
    text-decoration: none;
}

.jt-menu__secondary a:hover { color: var(--jt-accent); }

.jt-menu__foot {
    margin-top: auto;
    padding-top: var(--jt-6);
    display: grid;
    gap: var(--jt-3);
}

.jt-menu__signout {
    min-height: 44px;
    justify-self: start;
    border: 0;
    background: none;
    padding: 0;
    cursor: pointer;
}

.jt-menu a:focus-visible,
.jt-menu button:focus-visible {
    outline: 2px solid var(--jt-focus);
    outline-offset: 2px;
}

@media (prefers-reduced-motion: no-preference) {
    .jt-menu:not([hidden]) .jt-menu__scrim { animation: jt-menu-fade var(--jt-base) var(--jt-ease) both; }
    .jt-menu:not([hidden]) .jt-menu__panel { animation: jt-menu-in var(--jt-base) var(--jt-ease) both; }
}

@keyframes jt-menu-fade { from { opacity: 0; } }
@keyframes jt-menu-in { from { transform: translateX(-100%); } }


/* ==========================================================================
   The custom studio's live piece

   One chronograph case, five dials, crossfading in place with the light
   behind it taking each dial's colour. Callouts name the parts a commission
   decides. Beside the copy from 900px, under it below that.
   ========================================================================== */

.jt-studio-hero__grid {
    display: grid;
    gap: var(--jt-7);
    align-items: center;
}

@media (min-width: 900px) {
    .jt-studio-hero__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 0.9fr); }
}

.jt-studio {
    margin: 0;
    display: grid;
    gap: var(--jt-5);
    justify-items: center;
    --jt-glow: #e8c89a;
}

.jt-studio__stage {
    position: relative;
    width: min(100%, 440px);
    aspect-ratio: 4 / 5;
}

.jt-studio__glow {
    position: absolute;
    inset: 8% 0 12%;
    border-radius: 50%;
    background: radial-gradient(closest-side,
        color-mix(in srgb, var(--jt-glow) 75%, transparent) 0%,
        color-mix(in srgb, var(--jt-glow) 30%, transparent) 55%,
        transparent 100%);
    filter: blur(30px);
    transition: background 900ms var(--jt-ease);
}

.jt-studio__piece {
    position: absolute;
    inset: 4% 12% 6%;
    width: 76%;
    height: 90%;
    object-fit: contain;
    opacity: 0;
    transform: scale(0.97);
    filter: drop-shadow(0 30px 28px rgba(40, 28, 16, 0.22));
    transition: opacity 900ms var(--jt-ease), transform 1200ms var(--jt-ease);
}

.jt-studio__piece.is-on { opacity: 1; transform: none; }

@media (prefers-reduced-motion: no-preference) {
    .jt-studio__stage { animation: jt-studio-float 7s ease-in-out infinite; }
}

@keyframes jt-studio-float {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-10px); }
}

/* A sweep of light across the glass each time the dial changes. */
.jt-studio__stage::after {
    content: '';
    position: absolute;
    inset: 20% 20% 30%;
    border-radius: 50%;
    background: linear-gradient(110deg, transparent 35%, rgba(255, 255, 255, 0.45) 50%, transparent 65%);
    background-size: 300% 100%;
    background-position: 150% 0;
    opacity: 0;
    pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
    .jt-studio.is-changing .jt-studio__stage::after { animation: jt-studio-sheen 1100ms var(--jt-ease) both; }
}

@keyframes jt-studio-sheen {
    0%   { opacity: 0; background-position: 150% 0; }
    25%  { opacity: 1; }
    100% { opacity: 0; background-position: -50% 0; }
}

/* Callouts: a dot on the part, a hairline, the word. */
.jt-studio__call {
    position: absolute;
    display: flex;
    align-items: center;
    gap: 8px;
    font-family: var(--jt-sans);
    font-size: 10px;
    font-weight: 500;
    letter-spacing: var(--jt-track);
    text-transform: uppercase;
    color: var(--jt-ink);
    white-space: nowrap;
}

.jt-studio__call i {
    position: relative;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--jt-accent);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--jt-accent) 18%, transparent);
    flex: none;
}

.jt-studio__call::before {
    content: '';
    order: 1;
    width: 36px;
    height: 1px;
    background: var(--jt-ink-faint);
}

.jt-studio__call i { order: 0; }
.jt-studio__call { flex-direction: row; }
.jt-studio__call > :not(i) { order: 2; }

.jt-studio__call--case  { top: 20%; right: -2%; flex-direction: row-reverse; }
.jt-studio__call--strap { bottom: 12%; left: -4%; }

@media (prefers-reduced-motion: no-preference) {
    .jt-studio__call i::after {
        content: '';
        position: absolute;
        inset: -4px;
        border-radius: 50%;
        border: 1px solid var(--jt-accent);
        animation: jt-studio-pulse 2.4s ease-out infinite;
    }
}

@keyframes jt-studio-pulse {
    0%   { transform: scale(0.6); opacity: 0.9; }
    100% { transform: scale(2.4); opacity: 0; }
}

.jt-studio__panel {
    display: grid;
    justify-items: center;
    gap: var(--jt-3);
    text-align: center;
}

.jt-studio__swatches {
    display: flex;
    gap: var(--jt-2);
}

.jt-studio__swatch {
    width: 44px;
    height: 44px;
    display: grid;
    place-items: center;
    padding: 0;
    border: 1px solid transparent;
    border-radius: 50%;
    background: none;
    cursor: pointer;
    transition: border-color var(--jt-fast) var(--jt-ease-io);
}

.jt-studio__swatch span {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: radial-gradient(circle at 35% 30%, #fff 0%, var(--swatch) 38%, color-mix(in srgb, var(--swatch) 70%, #000) 100%);
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12);
    transition: transform var(--jt-fast) var(--jt-ease);
}

.jt-studio__swatch:hover span { transform: scale(1.1); }
.jt-studio__swatch[aria-checked='true'] { border-color: var(--jt-ink); }
.jt-studio__swatch:focus-visible { outline: 2px solid var(--jt-focus); outline-offset: 2px; }

.jt-studio__name {
    font-family: var(--jt-display);
    font-size: 20px;
    color: var(--jt-ink-strong);
}

@media (max-width: 560px) {
    .jt-studio__call { font-size: 9px; }
    .jt-studio__call::before { width: 20px; }
    .jt-studio__call--case { right: 0; }
    .jt-studio__call--strap { left: 0; }
}


/* ==========================================================================
   Footer: link groups as disclosures, and the phone layout

   On a desktop the groups are plain columns - the <details> is open and its
   summary is only a heading. On a phone the footer was a page of loose links;
   it is now the mark, a WhatsApp button, three headings that open, and the
   legal line, centred.
   ========================================================================== */

.jt-footer__mark { display: none; }

/* .jt-footer a paints every link muted, the button's label included. */
.jt-footer a.jt-btn--whatsapp,
.jt-footer a.jt-btn--whatsapp:hover { color: #fff; }
.jt-footer__whatsapp { display: none; }

.jt-footer__group details > summary {
    list-style: none;
    cursor: default;
}

.jt-footer__group details > summary::-webkit-details-marker { display: none; }
.jt-footer__group summary h2 { display: inline; }

@media (max-width: 600px) {
    .jt-footer {
        padding-block: var(--jt-8) calc(var(--jt-6) + env(safe-area-inset-bottom));
    }

    .jt-footer__cols {
        grid-template-columns: 1fr;
        gap: 0;
        margin-bottom: var(--jt-6);
    }

    .jt-footer__cols > div:first-child {
        display: flex;
        flex-direction: column;
        align-items: center;
        text-align: center;
        padding-bottom: var(--jt-6);
    }

    .jt-footer__mark {
        display: block;
        margin-bottom: var(--jt-4);
    }

    .jt-footer__mark img { display: block; width: 120px; height: auto; }

    /* The mark already says the name. */
    .jt-footer__brand { display: none; }

    .jt-footer__cols > div:first-child .jt-muted { margin-inline: auto !important; }

    .jt-footer__whatsapp {
        display: inline-flex;
        margin-top: var(--jt-4);
        min-width: 240px;
        justify-content: center;
    }

    .jt-footer__group {
        border-top: 1px solid var(--jt-hairline);
    }

    .jt-footer__group:last-child { border-bottom: 1px solid var(--jt-hairline); }

    .jt-footer__group details > summary {
        display: flex;
        align-items: center;
        justify-content: space-between;
        min-height: 56px;
        cursor: pointer;
    }

    .jt-footer__group summary h2 {
        margin: 0;
        font-size: 12px;
        color: var(--jt-ink-strong);
    }

    /* A plus that turns into a minus. */
    .jt-footer__group details > summary::after {
        content: '';
        width: 12px;
        height: 12px;
        background:
            linear-gradient(var(--jt-ink), var(--jt-ink)) center / 12px 1px no-repeat,
            linear-gradient(var(--jt-ink), var(--jt-ink)) center / 1px 12px no-repeat;
        transition: transform var(--jt-base) var(--jt-ease);
    }

    .jt-footer__group details[open] > summary::after {
        background: linear-gradient(var(--jt-ink), var(--jt-ink)) center / 12px 1px no-repeat;
        transform: rotate(180deg);
    }

    .jt-footer__group ul { padding-bottom: var(--jt-4); }

    .jt-footer__group li a {
        display: flex;
        align-items: center;
        min-height: 44px;
        font-size: 15px;
        color: var(--jt-ink);
    }

    .jt-footer__base {
        flex-direction: column;
        align-items: center;
        gap: var(--jt-3);
        text-align: center;
        border-top: 0;
        padding-top: 0;
    }
}


/* The refine panel's fields.

   They were paper-white boxes with a 1px grey border - the CMS's own input
   paint, which outranks a single class because it is written as
   `select, input[type='text'] { ... !important }`. On the cream panel a white
   box reads as a hole. These are a line under the value instead, which is how
   every other control in this theme is drawn. */
.jt-refine .jt-select--block,
.jt-refine .jt-input {
    min-height: 48px !important;
    padding: 12px 26px 12px 0 !important;
    background-color: transparent !important;
    border: 0 !important;
    border-bottom: 1px solid var(--jt-ink-faint) !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    color: var(--jt-ink-strong) !important;
    font-family: var(--jt-display);
    font-size: 16px !important;
    font-weight: 400 !important;
}

/* The chevron is two gradients meeting at a point; both have to sit where
   .jt-select--block puts them, or they meet as a broken glyph. */
.jt-refine .jt-select--block {
    background-position: calc(100% - 7px) center, calc(100% - 1px) center;
    background-size: 6px 6px;
    background-repeat: no-repeat;
}

.jt-refine .jt-input { padding-inline-end: 0 !important; }

.jt-refine .jt-select--block:hover,
.jt-refine .jt-input:hover { border-bottom-color: var(--jt-ink) !important; }

.jt-refine .jt-select--block:focus-visible,
.jt-refine .jt-input:focus,
.jt-refine .jt-input:focus-visible {
    border-bottom-color: var(--jt-accent) !important;
    outline: none;
}

.jt-refine .jt-label {
    display: block;
    margin-bottom: var(--jt-2);
}
