﻿/*
 * Charles Francis Studio - front-of-house design system
 *
 * Concept: THE PRINT SHOP COUNTER.
 * Rewritten 2026-09-04 from the gallery "viewing room" scheme to the model
 * Albert chose: The Stackhouse. Tokens in this file were measured off their
 * live site with getComputedStyle and are recorded in
 * notes/STACKHOUSE_DESIGN_MODEL_2026-09-04.md.
 *
 * What changed and why, because the difference is the whole point:
 *   - Ground is a warm white, not a warm grey. The page is bright.
 *   - One loud colour, #8B1E24, and it means "this is the action". Nothing
 *     decorative is ever painted in it.
 *   - Headings are a slab serif (Bitter). That is where the printerly feel
 *     comes from, and it is the single highest-leverage change in the file.
 *   - Buttons are sentence case Source Sans at 17px, 3px radius. The old
 *     uppercase letterspaced display buttons read as a museum, not a shop.
 *   - Numbers are no longer mono. A lab spec sheet is not what we are selling.
 *
 * Class names are unchanged from the previous system on purpose, so the six
 * page templates keep working through the swap.
 */

/* ---------------------------------------------------------------- tokens */

:root {
    /* Ink */
    --ink:        #1E1B18;  /* body text */
    --ink-soft:   #3D3833;
    --slate:      #635E58;  /* secondary text */
    /* Labels and captions. Was #767068, which is 4.67:1 on --ground but only
       4.37:1 on the --wall band, so every caption inside an alternating band
       missed AA. #726C64 is 4.96 / 4.64 / 5.19 on ground, wall and paper.
       Found by the audit run for the 2026-09-09 palette change. */
    --slate-soft: #726C64;

    /* Grounds */
    --ground:     #FCF9F8;  /* the page */
    --wall:       #F2F2F2;  /* alternating band */
    --wall-deep:  #E4E1DE;  /* rules and borders */
    --paper:      #FFFFFF;  /* cards, forms, anything representing a print */

    /* The one action colour. Buttons and the order link. Nothing else.
       Albert chose the mockups' red over the previous #DC391C on 2026-09-09.
       It is a straight upgrade on contrast: 8.68:1 as text on --ground against
       the orange's 4.32, and 9.09:1 for white on the fill against 4.52. */
    --action:      #8B1E24;
    --action-dark: #6E171C;  /* button hover. White on it measures 11.65:1 */
    /* The red passes as text AND as a fill, so the split the orange needed is
       gone: --action-ink is now the same colour, kept only so the seventeen
       rules that name it do not all have to change. Do not give it a value. */
    --action-ink:  var(--action);
    /* Red on the near-black footer is 1.88:1 and unreadable, so the accent on
       dark surfaces is the gold. 7.14:1 there. */
    --action-lift: var(--gold);
    /* The channels on their own, for rgba() focus rings. The ecommerce plugin's
       frontend.css reads this to tint its own; without it, it falls back to the
       gallery gold it used to hard-code. */
    --action-rgb: 139, 30, 36;

    /* Gold, from the 2026-09-09 style guide. It is a DARK-GROUND colour:
       #C9A24B measures 2.29:1 on --ground, which fails even the 3:1 minimum
       for icons, and 6.87:1 on charcoal. Anything gold that sits on a light
       ground uses --gold-ink instead, which is 4.82:1 on --ground. */
    --gold:      #C9A24B;
    --gold-ink:  #8A6A1F;
    --gold-rgb:  201, 162, 75;

    /* Two supporting colours, for banding sections only */
    --deep:      #25718A;   /* deep teal */
    --deep-dark: #1B5568;
    --sage:      #87ADAD;
    --cream:     #FEFAE3;   /* the guarantee badge */

    /* Aliases kept so nothing that still says "brass" breaks mid-sweep.
       They resolve to the action colour, NOT to the gold: every existing use of
       them is a button or a call to action, and moving them to gold would put
       gold type on a light ground. */
    --brass:      var(--action);
    --brass-lift: var(--action);

    /* Derived tints. Declared here rather than typed into the rules that use
       them, so the whole theme still moves from one block. */
    --on-deep:      #EAF3F5;  /* body text on the teal band */
    --on-deep-soft: #DCEDF1;
    --on-deep-quiet:#DAEEF2;
    --line:         #CFCAC4;  /* decorative hairlines and card edges */
    --line-soft:    #C9C4BE;
    /* Borders that ARE the control: inputs, selects, chips, the stepper, the
       menu button. WCAG wants 3:1 for those and --line is 1.55:1 on --ground.
       This is 3.32 / 3.47 / 3.10 on ground, paper and wall. Decorative rules
       keep --line, so the page does not get heavier. */
    --line-control: #8E8981;
    --foot-text:    #B9B4AE;
    --foot-link:    #E7E3DF;
    --cream-ink:    #5A4A12;  /* text on the cream badge */
    --cream-line:   #E8DFA8;
    --on-sage:      #23413F;  /* text on the sage band */
    --on-sage-soft: #253B3B;
    --hover-tint:   #FBF6F4;  /* the warm-white hover on a paper tile */

    --ok:         #2F6B4F;
    --alert:      #8C3A2E;

    /* Type */
    --display: 'Bitter', Georgia, 'Times New Roman', serif;
    --body:    'Source Sans 3', 'Source Sans Pro', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
    /* Numbers used to be mono. They are the same face as the body now, with
       tabular figures so columns still line up. */
    --data:    var(--body);

    /* Scale. Restrained: their H1 renders at 40px and their H2 at 32px. */
    --t-xs:   0.8125rem;   /* 13px - nav, eyebrows, captions */
    --t-sm:   0.9375rem;   /* 15px */
    --t-base: 1.0625rem;   /* 17px - body and buttons */
    --t-md:   1.1875rem;   /* 19px */
    --t-lg:   1.375rem;    /* 22px - lede */
    --t-xl:   1.625rem;    /* 26px - h3 */
    --t-2xl:  2rem;        /* 32px - h2 */
    --t-3xl:  2.5rem;      /* 40px - h1 */
    --t-4xl:  3rem;        /* 48px - home h1 only */

    /* Space, on a 4px grid */
    --s-1: 0.25rem;
    --s-2: 0.5rem;
    --s-3: 0.75rem;
    --s-4: 1rem;
    --s-5: 1.5rem;
    --s-6: 2rem;
    --s-7: 3rem;
    --s-8: 4rem;
    --s-9: 5.5rem;
    --s-10: 7rem;

    --measure: 40rem;      /* 640px reading measure */
    --measure-wide: 47.5rem; /* 760px */
    --frame: 90rem;        /* 1440px container */
    --gutter: clamp(1.25rem, 5vw, 3rem);

    --radius: 3px;

    --lift-1: 0 1px 2px rgba(30,27,24,.06), 0 2px 8px rgba(30,27,24,.05);
    --lift-2: 0 2px 4px rgba(30,27,24,.08), 0 12px 28px rgba(30,27,24,.09);

    --speed: 160ms;
    --ease: cubic-bezier(.2,.6,.3,1);
}

/* ------------------------------------------------------------------ base */

*, *::before, *::after { box-sizing: border-box; }

html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
}

body.studio {
    /* Column flex so a short page still puts the footer at the bottom of the
       window instead of leaving a band of bare ground under it. */
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    margin: 0;
    background: var(--ground);
    color: var(--ink);
    font-family: var(--body);
    font-size: var(--t-base);
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
}

body.studio > main { flex: 1 0 auto; }

.studio img { max-width: 100%; height: auto; display: block; }

.studio a { color: var(--ink); text-decoration-color: var(--action); text-underline-offset: 3px; }
.studio a:hover { text-decoration-color: var(--ink); }

.studio :focus-visible {
    outline: 2px solid var(--action);
    outline-offset: 3px;
}

/* Display voice: slab serif, sentence case. Bitter is where this site stops
   looking like a gallery and starts looking like a print shop. */
.studio h1, .studio h2, .studio h3, .studio h4,
.studio .display {
    font-family: var(--display);
    font-weight: 700;
    letter-spacing: -0.01em;
    line-height: 1.18;
    margin: 0 0 var(--s-4);
    text-wrap: balance;
    color: var(--ink);
}

.studio h1 { font-size: clamp(2rem, 4.5vw, var(--t-3xl)); line-height: 1.12; }
.studio h2 { font-size: clamp(1.625rem, 3vw, var(--t-2xl)); }
.studio h3 { font-size: var(--t-xl); }
.studio h4 { font-size: var(--t-md); }

.studio p { margin: 0 0 var(--s-4); max-width: var(--measure); }

/* The eyebrow names which part of the studio you are standing in. Stackhouse
   uses these above every capability block. */
.eyebrow {
    font-family: var(--body);
    font-size: var(--t-xs);
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--action-ink);
    margin: 0 0 var(--s-3);
    display: block;
}

.lede {
    font-size: clamp(var(--t-md), 2vw, var(--t-lg));
    line-height: 1.5;
    color: var(--slate);
    max-width: var(--measure-wide);
}

/* Numbers keep tabular figures so price columns line up, but they are set in
   the body face now. */
.num, .price, .studio table td.num {
    font-variant-numeric: tabular-nums;
}

.price { font-size: var(--t-md); font-weight: 600; }
/* The "from $x" that sits under a service hero headline. */
.price--hero { font-size: var(--t-xl); color: var(--action); margin-top: var(--s-5); }
.price .from {
    font-size: var(--t-sm);
    font-weight: 400;
    color: var(--slate-soft);
    margin-right: var(--s-1);
}

/* ---------------------------------------------------------------- layout */

.wrap {
    width: 100%;
    max-width: var(--frame);
    margin-inline: auto;
    padding-inline: var(--gutter);
}

.wrap--narrow { max-width: 56rem; }

.band { padding-block: clamp(var(--s-7), 7vw, var(--s-9)); }
.band--ground { background: var(--ground); }
.band--wall   { background: var(--wall); }
.band--deep   { background: var(--sage); }
.band--cream  { background: var(--cream); }
.band--tight  { padding-block: var(--s-6); }

/* The loud band. Deep teal, white type. Used for the guarantee, page headers
   and the closing call, and nowhere else. */
.band--ink { background: var(--deep); color: var(--on-deep); }
.band--ink h1, .band--ink h2, .band--ink h3, .band--ink h4 { color: var(--paper); }
.band--ink p { color: var(--on-deep-soft); }
.band--ink a { color: var(--paper); }
.band--ink .eyebrow { color: var(--on-deep-quiet); }
.band--ink .lede { color: var(--on-deep-soft); }

.band--deep .eyebrow { color: var(--on-sage); }
.band--deep p { color: var(--on-sage-soft); }

/* Lists inside the teal band. The order page numbers its four steps here and
   used to hard-code #C7C4BE and #fff into the markup to stay legible. */
.band--ink ul, .band--ink ol, .band--ink li { color: var(--on-deep-soft); }
.band--ink li strong { color: var(--paper); }
.band--ink li::marker { color: var(--on-deep-quiet); }

.band-head { margin-bottom: var(--s-7); max-width: var(--measure-wide); }
.band-head p { color: var(--slate); }

.grid { display: grid; gap: var(--s-5); }
/* min(Xrem, 100%) rather than a bare Xrem: auto-fit does NOT shrink a track
   below its own minimum, so `minmax(20rem, 1fr)` forced a 320px column inside a
   265px container and pushed /start-an-order 20px past a 320px viewport.
   Measured 2026-09-04. Every minmax track in this file carries the same guard. */
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr)); }

.split {
    display: grid;
    gap: clamp(var(--s-5), 5vw, var(--s-8));
    grid-template-columns: 1fr;
    align-items: center;
}
@media (min-width: 62rem) {
    .split { grid-template-columns: 1.05fr 1fr; }
    .split--flip > :first-child { order: 2; }
}

/* --------------------------------------------------------------- actions */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--s-2);
    font-family: var(--body);
    font-weight: 600;
    font-size: var(--t-base);
    letter-spacing: 0;
    text-transform: none;
    line-height: 1.2;
    white-space: nowrap;
    padding: 12px 24px;
    border: 1px solid transparent;
    border-radius: var(--radius);
    cursor: pointer;
    text-decoration: none;
    transition: background var(--speed) var(--ease),
                color var(--speed) var(--ease),
                border-color var(--speed) var(--ease);
}

.btn--lg { font-size: var(--t-md); padding: 15px 30px; }
.btn--sm { font-size: var(--t-sm); padding: 9px 18px; }

/* Every one of these is restated below as `.studio a.btn--x` too. Plugin
   stylesheets colour bare anchors with their own ink, which turned every
   primary button into a solid rectangle with invisible text (found in Chrome,
   2026-09-03). The duplicate rule is specificity, not sloppiness. */
.btn--primary { background: var(--action); color: var(--paper); border-color: var(--action); }
.btn--primary:hover { background: var(--action-dark); border-color: var(--action-dark); color: var(--paper); }

.btn--brass { background: var(--action); color: var(--paper); border-color: var(--action); }
.btn--brass:hover { background: var(--action-dark); border-color: var(--action-dark); color: var(--paper); }

.btn--ghost { background: transparent; color: var(--ink); border-color: var(--line-soft); }
.btn--ghost:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }

.band--ink .btn--ghost { color: var(--paper); border-color: rgba(255,255,255,.55); }
.band--ink .btn--ghost:hover { background: var(--paper); color: var(--deep); border-color: var(--paper); }

/* The quiet second door: a text link with an arrow, the way Stackhouse writes
   "See pricing" next to their primary button. */
.btn--text {
    background: transparent;
    border-color: transparent;
    color: var(--ink);
    padding-inline: var(--s-2);
    text-decoration: underline;
    text-decoration-color: var(--action);
    text-decoration-thickness: 2px;
    text-underline-offset: 4px;
}
.btn--text:hover { color: var(--action-ink); }
.band--ink .btn--text { color: var(--paper); text-decoration-color: rgba(255,255,255,.6); }

/* The two-door pattern: the person who is ready and the person who needs
   convincing are served side by side, neither blocking the other. */
.doors { display: flex; flex-wrap: wrap; gap: var(--s-3); align-items: center; margin-top: var(--s-5); }

/* --------------------------------------------------------------- promo bar */

.promobar {
    background: var(--action);
    color: var(--paper);
    text-align: center;
    font-size: var(--t-sm);
    font-weight: 600;
    padding: 9px var(--s-4);
}
.studio .promobar a { color: var(--paper); text-decoration-color: rgba(255,255,255,.6); }
.studio .promobar a:hover { text-decoration-color: var(--paper); }

/* --------------------------------------------------------------- trust bar */

/* One line under the hero. Place, scale, proof, offer. Stackhouse runs the
   same line and it does more work than a testimonials section. */
.trustbar {
    border-block: 1px solid var(--wall-deep);
    background: var(--paper);
    font-size: var(--t-sm);
    color: var(--slate);
}
.trustbar__in {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: var(--s-3) var(--s-5);
    padding-block: var(--s-4);
    text-align: center;
}
.trustbar__in > span { display: inline-flex; align-items: center; gap: var(--s-3); }
.trustbar__in > span:not(:last-child)::after {
    content: "";
    width: 4px; height: 4px;
    border-radius: 50%;
    background: var(--action);
}
.trustbar strong { color: var(--ink); font-weight: 600; }

/* ----------------------------------------------------------------- paper */

.paper {
    background: var(--paper);
    border: 1px solid var(--wall-deep);
    box-shadow: var(--lift-1);
    padding: var(--s-5);
    border-radius: var(--radius);
}

.paper--lift { box-shadow: var(--lift-2); }

.card {
    background: var(--paper);
    border: 1px solid var(--wall-deep);
    border-radius: var(--radius);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    text-decoration: none;
    color: inherit;
    transition: box-shadow var(--speed) var(--ease), border-color var(--speed) var(--ease);
}
.card:hover { box-shadow: var(--lift-2); border-color: var(--line); }
.card__media { aspect-ratio: 4 / 3; overflow: hidden; background: var(--wall); }
.card__media img { width: 100%; height: 100%; object-fit: cover; }
.card__body { padding: var(--s-5); display: flex; flex-direction: column; gap: var(--s-2); flex: 1; }
.card__body h3 { margin: 0; font-size: var(--t-md); }
.card__body p { margin: 0; font-size: var(--t-sm); color: var(--slate); }
.card__foot {
    margin-top: auto;
    padding-top: var(--s-4);
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s-3);
}
.card__go {
    font-size: var(--t-sm);
    font-weight: 600;
    color: var(--action-ink);
}

/* -------------------------------------------------- SIGNATURE: the stocks */

/*
 * A run of the actual stocks the studio prints on, each a real surface, named,
 * with its rate. It says "this is a lab" faster than any sentence could, and it
 * is the same control the ordering flow uses.
 */
.surfaces {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(10rem, 100%), 1fr));
    gap: 1px;
    background: var(--wall-deep);
    border: 1px solid var(--wall-deep);
    border-radius: var(--radius);
    overflow: hidden;
}

.surface {
    position: relative;
    background: var(--paper);
    padding: var(--s-4);
    display: flex;
    flex-direction: column;
    gap: var(--s-2);
    min-height: 11.5rem;
    text-decoration: none;
    color: inherit;
    transition: background var(--speed) var(--ease);
}
.surface:hover { background: var(--hover-tint); }

/* The swatch is drawn, not photographed, so it survives without asset work and
   still carries the right information: each stock gets its own grain.
   The hex values below are deliberately NOT tokens: they simulate seven real
   paper surfaces, so they belong to the stock and not to the theme. They are
   the only literal colours left outside :root. */
.surface__swatch {
    height: 4.25rem;
    border: 1px solid var(--wall-deep);
    border-radius: 2px;
    background-color: #F3F1EC;
    background-blend-mode: multiply;
}
/* When a real photograph of the stock exists, it takes the swatch's place at
   the same size. templates/studio/partials/surfaces.php decides which. */
.surface__swatch--photo {
    width: 100%;
    object-fit: cover;
    background: none;
}

.surface--luster .surface__swatch {
    background-image: radial-gradient(circle at 30% 30%, rgba(0,0,0,.06) 0 1px, transparent 1px);
    background-size: 4px 4px;
}
.surface--matte .surface__swatch {
    background-color: #F1EFE9;
    background-image: none;
}
.surface--baryta .surface__swatch {
    background-color: #FAF9F6;
    background-image: linear-gradient(115deg, rgba(0,0,0,.05) 0 1px, transparent 1px 3px);
    background-size: 5px 5px;
}
.surface--watercolour .surface__swatch {
    background-color: #F2EFE7;
    background-image:
        radial-gradient(circle at 20% 40%, rgba(0,0,0,.05) 0 2px, transparent 2px),
        radial-gradient(circle at 70% 70%, rgba(0,0,0,.04) 0 2px, transparent 2px);
    background-size: 9px 9px, 11px 11px;
}
.surface--canvas .surface__swatch {
    background-color: #F0EDE4;
    background-image:
        repeating-linear-gradient(0deg, rgba(0,0,0,.06) 0 1px, transparent 1px 4px),
        repeating-linear-gradient(90deg, rgba(0,0,0,.06) 0 1px, transparent 1px 4px);
}
.surface--metallic .surface__swatch {
    background-color: #EFEEEA;
    background-image: linear-gradient(100deg, rgba(255,255,255,.9) 0 12%, rgba(0,0,0,.07) 40%, rgba(255,255,255,.85) 70%, rgba(0,0,0,.05) 100%);
}
.surface--vinyl .surface__swatch {
    background-color: #EDEBE6;
    background-image: linear-gradient(0deg, rgba(0,0,0,.05) 0 50%, transparent 50%);
    background-size: 3px 3px;
}

.surface__name {
    font-family: var(--display);
    font-weight: 700;
    font-size: var(--t-base);
    line-height: 1.2;
}
.surface__rate {
    font-size: var(--t-sm);
    font-weight: 600;
    color: var(--action-ink);
    font-variant-numeric: tabular-nums;
    margin-top: auto;
}
.surface__note { font-size: var(--t-sm); color: var(--slate); line-height: 1.4; }

/* ------------------------------------------------------------ spec block */

/* A scannable spec list high on every service page, turnaround included. It
   answers the six questions people actually ask. */
.spec {
    font-size: var(--t-sm);
    border-top: 1px solid var(--wall-deep);
    margin: var(--s-6) 0;
}
.spec div {
    display: grid;
    grid-template-columns: 12rem 1fr;
    gap: var(--s-4);
    padding: var(--s-3) 0;
    border-bottom: 1px solid var(--wall-deep);
}
.spec dt, .spec .k {
    color: var(--slate-soft);
    text-transform: uppercase;
    font-size: var(--t-xs);
    font-weight: 700;
    letter-spacing: 0.08em;
}
/* Long unbreakable values (an email address, a street address) pushed the whole
   card past the viewport at 320px. Measured on /start-an-order: 20px over. */
.spec dd, .spec .v { margin: 0; color: var(--ink); min-width: 0; overflow-wrap: anywhere; }
@media (max-width: 40rem) {
    .spec div { grid-template-columns: 1fr; gap: var(--s-1); }
}

/* ---------------------------------------------------- includes checklist */

/* "Every job includes", written as a list rather than a promise in prose.
   Stackhouse publishes seven of these on their scanning page. */
.checks { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-3); max-width: var(--measure-wide); }
.checks li {
    display: grid;
    grid-template-columns: 1.5rem 1fr;
    gap: var(--s-3);
    align-items: start;
    font-size: var(--t-base);
}
.checks li::before {
    content: "";
    width: 1.125rem; height: 1.125rem;
    margin-top: .28em;
    border-radius: 50%;
    background: var(--action);
    /* A tick, drawn, so no icon font is needed */
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M10 0a10 10 0 100 20A10 10 0 0010 0zm4.7 7.3l-5.4 6a1 1 0 01-1.5 0L5.3 10.6a1 1 0 011.4-1.4l2 2 4.6-5.2a1 1 0 011.4 1.3z'/%3E%3C/svg%3E") center / contain no-repeat;
            mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M10 0a10 10 0 100 20A10 10 0 0010 0zm4.7 7.3l-5.4 6a1 1 0 01-1.5 0L5.3 10.6a1 1 0 011.4-1.4l2 2 4.6-5.2a1 1 0 011.4 1.3z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.band--ink .checks li::before { background: var(--paper); }

/* A ticked list inside a centred band: centre the BLOCK, not the lines. Ragged
   centred text beside a fixed left-hand tick column reads as broken. */
.center .checks { margin-inline: auto; max-width: 34rem; text-align: left; }

/* ------------------------------------------------------------- benefits */

/* Four short lines under the hero. Not cards, not icons: statements. */
.benefits { display: grid; gap: var(--s-5); grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr)); }
.benefit h3 { font-size: var(--t-md); margin-bottom: var(--s-2); }
.benefit p { font-size: var(--t-sm); color: var(--slate); margin: 0; }
.benefit { border-top: 3px solid var(--action); padding-top: var(--s-4); }

/* ---------------------------------------------------------------- steps */

/* "How it works", numbered. */
.steps { list-style: none; counter-reset: step; margin: 0; padding: 0; display: grid; gap: var(--s-6); grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr)); }
.steps li { counter-increment: step; }
.steps li::before {
    content: counter(step);
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.5rem; height: 2.5rem;
    border-radius: 50%;
    background: var(--action);
    color: var(--paper);
    font-family: var(--display);
    font-weight: 700;
    font-size: var(--t-md);
    margin-bottom: var(--s-3);
}
.steps h3 { font-size: var(--t-md); margin-bottom: var(--s-2); }
.steps p { font-size: var(--t-sm); color: var(--slate); margin: 0; }
.band--ink .steps p { color: var(--on-deep-soft); }
.band--ink .steps li::before { background: var(--paper); color: var(--deep); }

/* ------------------------------------------------------------------ FAQ */

.faq { max-width: var(--measure-wide); border-top: 1px solid var(--wall-deep); }
.faq details { border-bottom: 1px solid var(--wall-deep); }
.faq summary {
    cursor: pointer;
    list-style: none;
    padding: var(--s-4) 2.5rem var(--s-4) 0;
    font-family: var(--display);
    font-weight: 700;
    font-size: var(--t-md);
    position: relative;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
    content: "+";
    position: absolute;
    right: var(--s-2);
    top: 50%;
    transform: translateY(-50%);
    font-family: var(--body);
    font-weight: 400;
    font-size: 1.5rem;
    color: var(--action);
    line-height: 1;
}
.faq details[open] summary::after { content: "\2013"; }
.faq details > *:not(summary) { margin-bottom: var(--s-4); color: var(--slate); max-width: var(--measure); }

/* --------------------------------------------------------- price tables */

.ptable { width: 100%; border-collapse: collapse; font-size: var(--t-sm); font-variant-numeric: tabular-nums; }
.ptable caption {
    text-align: left;
    font-family: var(--display);
    font-weight: 700;
    font-size: var(--t-md);
    margin-bottom: var(--s-3);
}
.ptable th, .ptable td { padding: var(--s-3) var(--s-4); text-align: right; }
.ptable th:first-child, .ptable td:first-child { text-align: left; }
.ptable thead th {
    font-size: var(--t-xs);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--slate-soft);
    border-bottom: 1px solid var(--ink);
}
.ptable tbody tr { border-bottom: 1px solid var(--wall-deep); }
.ptable tbody tr:hover { background: rgba(var(--action-rgb), .05); }
.table-scroll { overflow-x: auto; }

/* ------------------------------------------------------------ calculator */

/* Read-only. It prices from the same rate card the tables below it publish,
   and it deliberately does not add anything to a cart. */
.calc {
    display: grid;
    gap: var(--s-5);
    grid-template-columns: 1fr;
    background: var(--paper);
    border: 1px solid var(--wall-deep);
    border-radius: var(--radius);
    padding: var(--s-5);
    max-width: 56rem;
}
@media (min-width: 52rem) { .calc { grid-template-columns: 1.15fr .85fr; } }

.calc__fields { display: grid; gap: var(--s-4); grid-template-columns: repeat(2, 1fr); align-content: start; }
.calc__fields .calc__field--wide { grid-column: 1 / -1; }
.calc__field label {
    display: block;
    font-size: var(--t-xs);
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--slate-soft);
    margin-bottom: var(--s-2);
}
.calc__field input[type="number"],
.calc__field select {
    width: 100%;
    padding: 10px 12px;
    border: 1px solid var(--line-control);
    border-radius: var(--radius);
    font-family: var(--body);
    font-size: var(--t-base);
    background: var(--paper);
    color: var(--ink);
}
.calc__check { display: flex; align-items: center; gap: var(--s-2); font-size: var(--t-sm); }
.calc__check input { width: 1.05rem; height: 1.05rem; accent-color: var(--action); }

.calc__out { background: var(--wall); border-radius: var(--radius); padding: var(--s-5); align-self: start; }
.calc__row { display: flex; justify-content: space-between; gap: var(--s-3); padding-block: var(--s-2); font-size: var(--t-sm); color: var(--slate); font-variant-numeric: tabular-nums; }
.calc__row span:last-child { color: var(--ink); font-weight: 600; }
.calc__total {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--s-3);
    margin-top: var(--s-3);
    padding-top: var(--s-3);
    border-top: 1px solid var(--wall-deep);
    font-family: var(--display);
    font-weight: 700;
    font-size: var(--t-xl);
    font-variant-numeric: tabular-nums;
}
.calc__note { font-size: var(--t-xs); color: var(--slate-soft); margin: var(--s-3) 0 0; }
.calc__flag { font-size: var(--t-xs); color: var(--action-ink); font-weight: 600; margin: var(--s-2) 0 0; }

/* ------------------------------------------------------------- assurance */

/* Proof sits next to the action, never in a testimonials graveyard at the
   bottom. Facts, no icons. */
.assure {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr));
    gap: var(--s-5);
    border-top: 1px solid var(--wall-deep);
    border-bottom: 1px solid var(--wall-deep);
    padding-block: var(--s-5);
}
.assure__n {
    font-family: var(--display);
    font-weight: 700;
    font-size: var(--t-xl);
    line-height: 1;
    display: block;
    margin-bottom: var(--s-2);
    color: var(--action-ink);
}
.assure__l { font-size: var(--t-sm); color: var(--slate); }

/* ------------------------------------------------------------- guarantee */

.badge {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    background: var(--cream);
    border: 1px solid var(--cream-line);
    border-radius: var(--radius);
    padding: 7px 14px;
    font-size: var(--t-sm);
    font-weight: 600;
    color: var(--cream-ink);
    margin-bottom: var(--s-4);
}

/* ---------------------------------------------------------------- header */

.site-head {
    position: sticky;
    top: 0;
    z-index: 40;
    background: var(--paper);
    color: var(--ink);
    border-bottom: 1px solid var(--wall-deep);
}
.site-head__bar {
    display: flex;
    align-items: center;
    gap: var(--s-5);
    padding-block: var(--s-3);
    min-height: 68px;
}
.site-head__logo { display: flex; align-items: center; gap: var(--s-3); text-decoration: none; flex: 0 0 auto; }
.site-head__logo img { height: 40px; width: auto; }
.site-head__word { display: block; line-height: 1; white-space: nowrap; }
/* The script wordmark, from the 2026-09-09 style guide. It carries the full
   --gold rather than --gold-ink, which would fail as body text on this ground:
   WCAG exempts a logotype from the contrast minimum, and this string is the
   brand name. Nothing else on a light ground is allowed to use --gold.
   Allura is a stand-in until Albert supplies the real logo file. */
.site-head__script {
    display: block;
    font-family: 'Allura', 'Brush Script MT', cursive;
    font-weight: 400;
    font-size: 1.85rem;
    line-height: .95;
    color: var(--gold);
    /* Allura sits high in its box and the descenders need room. */
    padding-block: 2px;
}
.site-head__sub {
    display: block;
    color: var(--slate-soft);
    letter-spacing: .3em;
    font-size: 0.625rem;
    text-transform: uppercase;
    font-family: var(--body);
    font-weight: 600;
    /* Optically centred under the script, which starts with a wide capital. */
    padding-left: .35em;
}

/* -------------------------------------------------------------- main nav */

/* Seven items, three of which open a panel. The bar names the services rather
   than hiding them behind "Services", because a visitor scanning for the word
   "Restoration" should find it in the bar. Sized down from the four-item nav so
   seven labels fit between the wordmark and the utility group at 1024px. */
.site-nav { margin-left: auto; }
.site-nav > ul { display: flex; gap: var(--s-4); list-style: none; margin: 0; padding: 0; align-items: center; }
.site-nav > ul > li { position: relative; }
.site-nav a, .site-nav .navgroup__btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-family: var(--body);
    font-size: var(--t-xs);
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink);
    text-decoration: none;
    background: none;
    border: 0;
    padding: var(--s-2) 0;
    border-bottom: 2px solid transparent;
    cursor: pointer;
}
.site-nav a:hover, .site-nav a[aria-current="page"], .site-nav .navgroup__btn:hover {
    color: var(--action-ink);
    border-bottom-color: var(--action);
}
.site-nav .navgroup__btn[aria-expanded="true"], .site-nav .navgroup__btn.is-current { color: var(--action-ink); border-bottom-color: var(--action); }
.site-nav .navgroup__btn::after {
    content: "";
    width: 0; height: 0;
    border: 4px solid transparent;
    border-top-color: currentColor;
    margin-top: 3px;
}

/* Ordering stays prominent in the main menu. */
.studio .site-nav a.navcta {
    color: var(--action);
    font-weight: 800;
}
.studio .site-nav a.navcta:hover { color: var(--action-dark); }

/* Reuse the site's previous orange for the estimate action. */
.studio .site-head .site-head__estimate {
    background: #DC391C;
    border-color: #DC391C;
    color: #fff;
}
.studio .site-head .site-head__estimate:hover {
    background: #B82F17;
    border-color: #B82F17;
}

/* The one action in the header, on the right, filled. */
.studio .site-head__cta {
    padding: 10px 18px;
    font-size: var(--t-sm);
    white-space: nowrap;
    flex: 0 0 auto;
}

.navgroup__panel {
    position: absolute;
    top: 100%;
    left: calc(-1 * var(--s-3));
    min-width: 15rem;
    background: var(--paper);
    border: 1px solid var(--wall-deep);
    border-radius: var(--radius);
    box-shadow: var(--lift-2);
    padding: var(--s-3);
    margin: 0;
    list-style: none;
    display: none;
    z-index: 50;
}
.navgroup__panel li { margin: 0; }
.studio .navgroup__panel a {
    display: block;
    padding: var(--s-2) var(--s-3);
    font-size: var(--t-sm);
    font-weight: 500;
    letter-spacing: 0;
    text-transform: none;
    border-bottom: 0;
    border-radius: 2px;
    color: var(--ink);
}
.studio .navgroup__panel a:hover { background: var(--wall); color: var(--action-ink); }
.navgroup.is-open .navgroup__panel { display: block; }

.site-head__utility { display: flex; align-items: center; gap: var(--s-4); flex: 0 0 auto; }

/* Account and cart. Glyphs, not words: the header is already tight at 1024px.
   The cart only grows a number when it holds something. */
.studio .site-head__icon {
    position: relative;
    display: inline-flex;
    align-items: center;
    color: var(--ink);
    text-decoration: none;
    padding: var(--s-1);
}
.studio .site-head__icon:hover { color: var(--action-ink); }
.site-head__count {
    position: absolute;
    top: -4px;
    right: -7px;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    border-radius: 8px;
    background: var(--action);
    color: var(--paper);
    font-size: 10px;
    font-weight: 700;
    line-height: 16px;
    text-align: center;
}

.studio .site-head .site-head__phone {
    font-size: var(--t-sm);
    font-weight: 600;
    color: var(--ink);
    text-decoration: none;
    white-space: nowrap;
    display: inline-block;
}
.studio .site-head .site-head__phone:hover { color: var(--action-ink); }

.navtoggle {
    display: none;
    background: none;
    border: 1px solid var(--line-control);
    color: var(--ink);
    border-radius: var(--radius);
    padding: var(--s-2) var(--s-3);
    font-family: var(--body);
    font-size: var(--t-sm);
    font-weight: 600;
    cursor: pointer;
}

/* Seven labels, a wordmark, two icons and a filled button do not fit on one
   1024px line at the full nav size. Tighten before the bar has to wrap. */
@media (max-width: 82rem) {
    .site-nav > ul { gap: var(--s-3); }
    .site-nav a, .site-nav .navgroup__btn { font-size: 0.75rem; letter-spacing: 0.04em; }
    .studio .site-head .site-head__phone { display: none; }
}

@media (max-width: 64rem) {
    .navtoggle { display: block; margin-left: auto; }
    .site-nav { display: none; width: 100%; margin: 0; }
    .site-nav.is-open { display: block; max-height: 50vh; max-height: 50dvh; overflow-y: auto; }
    .site-nav > ul { flex-direction: column; align-items: stretch; gap: 0; padding-bottom: var(--s-4); }
    .site-nav a, .site-nav .navgroup__btn { display: flex; width: 100%; padding: var(--s-3) 0; border-bottom: 1px solid var(--wall-deep); justify-content: space-between; }
    .studio .site-nav a.navcta { justify-content: center; margin-top: var(--s-3); }
    .navgroup__panel { position: static; min-width: 0; display: none; border: 0; box-shadow: none; padding: 0 0 var(--s-3) var(--s-4); }
    .site-nav .navgroup__panel a { white-space: normal; }
    .site-head__bar { flex-wrap: wrap; }
    .site-head__utility { order: 3; }
}

/* Below a phone's width the order button is the last thing that has to fit. */
@media (max-width: 30rem) {
    .studio .site-head__bar { gap: var(--s-3); }
    .studio .site-head__utility { gap: var(--s-2); }
    .studio .site-head .site-head__phone { display: none; }
    .studio .site-head__logo img { height: 32px; }
    .studio .site-head__word { font-size: var(--t-base); }
}

/* Section bar: a page's own contents, pinned, with the order button always in
   reach. */
/* Pinned directly under the header. The offset CANNOT be a constant: below
   60rem .site-head__bar wraps (see the media query above) and the header grows
   a second row, so a hard-coded 68px parked this bar UNDERNEATH the header and
   left only its bottom sliver visible on a phone. footer.php measures the real
   header and writes --head-h; the literal here is the no-JavaScript fallback
   and is the desktop single-row height. */
.secbar {
    position: sticky;
    top: var(--head-h, 68px);
    z-index: 30;
    background: var(--ground);
    border-bottom: 1px solid var(--wall-deep);
}
/* The contents strip scrolls sideways on a phone rather than wrapping, because
   wrapping it would push the sticky header taller and every anchor offset with
   it. Keeping the scroll but showing the browser's scrollbar track under it
   looked unfinished, so the track is hidden and a fade on the right edge says
   there is more. Decided 2026-09-10 after seeing it at 390px. */
.secbar__in {
    display: flex;
    align-items: center;
    gap: var(--s-5);
    padding-block: var(--s-3);
    overflow-x: auto;
    scrollbar-width: none;              /* Firefox */
    -ms-overflow-style: none;           /* old Edge */
    /* The fade is drawn on the strip itself, so it moves with the sticky bar
       and never covers the button at the end of the row on wide screens. */
    -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 2.5rem), transparent 100%);
            mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 2.5rem), transparent 100%);
}
.secbar__in::-webkit-scrollbar { display: none; }
/* No fade once everything fits: above the wrap point the row is not scrollable. */
@media (min-width: 64rem) {
    .secbar__in { -webkit-mask-image: none; mask-image: none; }
}
.secbar a {
    font-size: var(--t-sm);
    font-weight: 600;
    text-decoration: none;
    color: var(--slate);
    white-space: nowrap;
}
.secbar a:hover { color: var(--action-ink); }
.secbar .btn { margin-left: auto; }
.studio .secbar .btn--primary { background: var(--action); color: var(--paper); border-color: var(--action); }
.studio .secbar .btn--primary:hover { background: var(--action-dark); border-color: var(--action-dark); }

/* Anchored targets must clear the sticky chrome when jumped to. Not only
   sections: the price list anchors a table wrapper. --chrome-h is header plus
   section bar, measured in footer.php; the fallback is the desktop sum. */
.studio [id] { scroll-margin-top: calc(var(--chrome-h, 114px) + 1rem); }

/* ----------------------------------------------------------------- hero */

/* Light, not the ink slab it used to be. The headline carries the page. */
.hero {
    background: var(--ground);
    color: var(--ink);
    position: relative;
    overflow: hidden;
}
.hero__in {
    display: grid;
    gap: var(--s-7);
    /* Was clamp(3rem, 7vw, 5.5rem), which put 204px of air into a 1456px hero
       and pushed the first action towards the fold. Albert asked for a shorter
       hero on 2026-09-09; this is where most of the height was. */
    padding-block: clamp(var(--s-6), 4.5vw, var(--s-8));
    align-items: center;
}
@media (min-width: 62rem) { .hero__in { grid-template-columns: 1.05fr .95fr; } }

/* A hero with no photograph: one column, held to a reading measure, rather than
   text in the left half and a hole in the right. */
@media (min-width: 62rem) {
    .hero--text .hero__in { grid-template-columns: minmax(0, 46rem); }
}
.hero--text h1, .hero--text .lede, .hero--text p { max-width: 42rem; }

.hero h1 { color: var(--ink); font-size: clamp(2.125rem, 5vw, var(--t-4xl)); }
.hero .lede { color: var(--slate); }
.hero__mark {
    width: 100%;
    max-width: 22rem;
    justify-self: center;
}

/* Service hero photograph. Sits in the second column, cropped to a tall-ish
   landscape so the headline keeps the left half at every width. */
.studio .hero__photo {
    width: 100%;
    height: 100%;
    max-height: 26rem;
    object-fit: cover;
    border-radius: var(--radius);
    box-shadow: var(--lift-2);
}
@media (max-width: 62rem) {
    .studio .hero__photo { max-height: 16rem; }
}

/* ------------------------------------------- the full-bleed hero (2026-09-10)

   The second of the two shapes Albert chose from the mockups. A photograph
   bled to the edges, a scrim over it, the words on top. It is shorter than the
   split hero on purpose: the mockups' heroes are about 420px, ours were closer
   to 700, and the whole point of the change was to get the page's first action
   above the fold.

   The scrim is two gradients rather than a flat tint: a strong wash on the left
   where the type sits, and a light overall darkening so the picture still reads
   as a picture. White on the left third measures well past AA on every one of
   the three photographs used; the flat 45% tint that was tried first did not.  */

.hero--full {
    position: relative;
    isolation: isolate;
    background: var(--ink);
    color: var(--paper);
    overflow: hidden;
    min-height: 30rem;
    display: flex;
    align-items: center;
    /* The inner grid carries the padding. Doubling it here is what made the
       first version 764px tall. */
    padding-block: 0;
}
.hero--full .hero__in { padding-block: var(--s-6); width: 100%; }
.studio .hero--full .hero__bg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    z-index: -2;
    /* Nothing here is a photograph of this studio yet, so it is deliberately
       quiet: it sets a mood behind the words rather than claiming to be proof. */
    filter: saturate(.9);
}
.hero--full::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background:
        linear-gradient(90deg, rgba(20,16,14,.92) 0%, rgba(20,16,14,.78) 38%, rgba(20,16,14,.30) 72%, rgba(20,16,14,.18) 100%),
        linear-gradient(180deg, rgba(20,16,14,.35) 0%, rgba(20,16,14,.15) 45%, rgba(20,16,14,.45) 100%);
}
.hero--full .hero__in { grid-template-columns: 1fr; }
@media (min-width: 62rem) {
    /* Wide enough for three buttons on one line; the type inside is held to a
       reading measure separately so the column does not set the line length. */
    .hero--full .hero__in { grid-template-columns: minmax(0, 38rem) 1fr; }
}
.studio .hero--full h1,
.studio .hero--full .lede,
.studio .hero--full p { max-width: 34rem; }
.studio .hero--full h1 { color: var(--paper); }
.studio .hero--full .lede { color: rgba(255,255,255,.90); }
.studio .hero--full p { color: rgba(255,255,255,.86); }
.studio .hero--full .eyebrow { color: var(--gold); }
.studio .hero--full .price { color: var(--paper); }
.studio .hero--full .price .from { color: rgba(255,255,255,.72); }
.studio .hero--full .badge {
    background: rgba(255,255,255,.14);
    border-color: rgba(255,255,255,.34);
    color: var(--paper);
}
/* The ghost button needs a light edge on a dark ground, the same treatment the
   teal band already gets. */
.studio .hero--full .btn--text { color: var(--paper); text-decoration-color: rgba(255,255,255,.6); }
.studio .hero--full .btn--ghost { color: var(--paper); border-color: rgba(255,255,255,.55); }
.studio .hero--full .btn--ghost:hover { background: var(--paper); color: var(--ink); border-color: var(--paper); }

/* ------------------------------------------------ before and after slider

   Two images stacked in one box. The one on top is clipped at --ba-pos, which
   a real range input drives, so the handle works from the keyboard and is
   announced. Nothing here renders until real pairs exist on disk.            */

.bagrid {
    display: grid;
    gap: var(--s-5);
    /* Two up at most. One pair on its own used to take the full container and
       stand about 900px tall, which read as a hero rather than an example. */
    grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
    max-width: 60rem;
}
.studio .ba { margin: 0; }
.ba__frame {
    position: relative;
    overflow: hidden;
    border-radius: var(--radius);
    background: var(--wall);
    line-height: 0;
    box-shadow: var(--lift-1);
    /* Every pair the same shape, whatever the two files measure. */
    aspect-ratio: 4 / 3;
}
.studio .ba__img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.studio .ba__img--after {
    position: absolute;
    inset: 0;
    /* Everything to the right of the handle is the finished version. */
    clip-path: inset(0 0 0 var(--ba-pos));
}
.ba__line {
    position: absolute;
    top: 0;
    bottom: 0;
    left: var(--ba-pos);
    width: 2px;
    margin-left: -1px;
    background: var(--paper);
    box-shadow: 0 0 0 1px rgba(30,27,24,.35);
    pointer-events: none;
}
.ba__tag {
    position: absolute;
    bottom: var(--s-3);
    padding: 3px 9px;
    font-size: var(--t-xs);
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    line-height: 1.6;
    border-radius: 2px;
    color: var(--paper);
    pointer-events: none;
}
.ba__tag--before { left: var(--s-3); background: rgba(30,27,24,.78); }
.ba__tag--after  { right: var(--s-3); background: var(--action); }
/* The control fills the picture: the whole image is the drag surface, and the
   thumb is invisible because .ba__line draws the handle. */
.studio .ba__range {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    background: transparent;
    border: 0;
    appearance: none;
    -webkit-appearance: none;
    cursor: ew-resize;
}
.studio .ba__range::-webkit-slider-thumb { -webkit-appearance: none; width: 44px; height: 100%; background: transparent; cursor: ew-resize; }
.studio .ba__range::-moz-range-thumb { width: 44px; height: 100%; background: transparent; border: 0; cursor: ew-resize; }
.studio .ba__range::-webkit-slider-runnable-track { background: transparent; }
.studio .ba__range::-moz-range-track { background: transparent; }
.studio .ba__range:focus-visible { outline: 3px solid var(--action); outline-offset: 2px; }
.studio .ba figcaption { display: block; margin-top: var(--s-3); line-height: 1.5; }
.studio .ba figcaption strong { display: block; font-family: var(--display); }
.studio .ba figcaption span { font-size: var(--t-sm); color: var(--slate); }
.ba__empty { color: var(--slate); max-width: var(--measure); }

/* ---------------------------------------------------- ready-made presets

   Cards that open the builder with a size and a paper already chosen. They are
   links, not buttons: the whole card is the target, and the price on it is
   computed by the same function as the published price table.                */

.presets {
    display: grid;
    gap: var(--s-4);
    grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
}
.studio a.preset {
    display: flex;
    flex-direction: column;
    gap: var(--s-1);
    padding: var(--s-5);
    background: var(--paper);
    border: 1px solid var(--wall-deep);
    border-radius: var(--radius);
    text-decoration: none;
    color: var(--ink);
    transition: box-shadow var(--speed) var(--ease), border-color var(--speed) var(--ease), transform var(--speed) var(--ease);
}
.studio a.preset:hover {
    border-color: var(--action);
    box-shadow: var(--lift-2);
    transform: translateY(-2px);
}
.preset__size { font-family: var(--display); font-weight: 700; font-size: var(--t-xl); line-height: 1.1; }
.preset__stock { font-size: var(--t-sm); font-weight: 600; color: var(--slate); }
.preset__note { font-size: var(--t-xs); color: var(--slate-soft); margin-top: var(--s-1); }
.preset__price { font-family: var(--display); font-weight: 700; font-size: var(--t-lg); color: var(--action); margin-top: var(--s-3); }
.preset__go {
    font-size: var(--t-xs);
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--action);
    margin-top: var(--s-2);
}
.studio a.preset:hover .preset__go { text-decoration: underline; }
.preset__foot { margin-top: var(--s-4); font-size: var(--t-sm); color: var(--slate-soft); }

/* Three verbs side by side on the home page, stacking on a phone. */
.doors--three { flex-wrap: wrap; gap: var(--s-3); }
.studio .doors--three .btn { flex: 0 1 auto; }
.hero__aside { margin-top: var(--s-4); font-size: var(--t-sm); }
.studio .hero--full .hero__aside { color: rgba(255,255,255,.80); }
.studio .hero--full .hero__aside a { color: var(--paper); text-decoration-color: rgba(255,255,255,.6); }
@media (max-width: 30rem) {
    .doors--three { flex-direction: column; align-items: stretch; }
    .studio .doors--three .btn { justify-content: center; }
}

@media (max-width: 48rem) {
    .hero--full { min-height: 0; padding-block: var(--s-7); }
    /* On a phone the picture is behind three lines of type and a button, so the
       left-hand wash has to cover the whole width instead of a third of it. */
    .hero--full::before {
        background:
            linear-gradient(180deg, rgba(20,16,14,.86) 0%, rgba(20,16,14,.80) 60%, rgba(20,16,14,.88) 100%);
    }
}

/* Page header for the plain and index pages: a teal slab with the title in it. */
.pagehead { background: var(--deep); color: var(--on-deep); }
.pagehead h1 { color: var(--paper); }

/* --------------------------------------------------------------- footer */

.site-foot { background: var(--ink); color: var(--foot-text); padding-block: var(--s-8) var(--s-6); }
.studio .site-foot a { color: var(--foot-link); text-decoration: none; }
.studio .site-foot a:hover { color: var(--action-lift); text-decoration: underline; }
.site-foot h4 {
    font-family: var(--display);
    color: var(--paper);
    font-size: var(--t-sm);
    letter-spacing: .04em;
    margin-bottom: var(--s-4);
}
.site-foot ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-2); font-size: var(--t-sm); }
.site-foot__grid { display: grid; gap: 2rem 4rem; grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr); align-items: start; }
.site-foot__studio { grid-column: 1; grid-row: 1; }
.site-foot__browse { grid-column: 2; min-width: 0; }
.site-foot__quick { display: flex; flex-wrap: wrap; gap: .5rem 1.5rem; margin-bottom: 1rem; font-size: var(--t-sm); font-weight: 600; }
.site-foot__quick a { display: inline-flex; align-items: center; gap: .75rem; min-height: 44px; }
.site-foot__group { border-bottom: 1px solid rgba(255,255,255,.18); }
.site-foot__group summary { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 1rem 0; color: var(--paper); font-weight: 600; cursor: pointer; list-style: none; }
.site-foot__group summary::-webkit-details-marker { display: none; }
.site-foot__group summary::after { content: '+'; font-size: 1.25rem; font-weight: 400; }
.site-foot__group[open] summary::after { content: '−'; }
.site-foot__group ul { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .25rem 1.5rem; padding-bottom: 1rem; }
.site-foot__group li a { display: inline-block; padding-block: .4rem; }
.site-foot summary:focus-visible, .site-foot a:focus-visible { outline: 2px solid var(--paper); outline-offset: 4px; }
@media (max-width: 42rem) {
    .site-foot__grid { grid-template-columns: minmax(0, 1fr); gap: 2rem; }
    .site-foot__browse { grid-column: 1; }
    .site-foot__studio { grid-row: auto; }
    .site-foot__group ul { grid-template-columns: minmax(0, 1fr); }
}
.site-foot__legal {
    margin-top: var(--s-7);
    padding-top: var(--s-4);
    border-top: 1px solid rgba(255,255,255,.14);
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-4);
    justify-content: space-between;
    font-size: var(--t-xs);
}
.site-foot address { font-style: normal; font-size: var(--t-sm); line-height: 1.7; }
.site-foot p { color: var(--foot-text); }
.site-foot__studio-details { display:grid; gap:.5rem; overflow-wrap:anywhere; }
.site-foot .site-foot__studio-details > * { margin:0; font-size:.9375rem; line-height:1.7; }

/* ----------------------------------------------------------------- misc */

.prose { max-width: var(--measure-wide); }
.prose h2 { margin-top: var(--s-7); }
.prose ul, .prose ol { max-width: var(--measure-wide); padding-left: 1.2em; }
.prose li { margin-bottom: var(--s-2); }

.tag {
    display: inline-block;
    font-size: var(--t-xs);
    font-weight: 600;
    letter-spacing: .04em;
    padding: 3px var(--s-2);
    border-radius: 2px;
    background: var(--wall);
    color: var(--slate);
}
.tag--brass { background: rgba(var(--action-rgb), .10); color: var(--action-ink); }

.rule { border: 0; border-top: 1px solid var(--wall-deep); margin-block: var(--s-7); }

.center { text-align: center; }
.center p { margin-inline: auto; }
.center .doors { justify-content: center; }
.center .band-head { margin-inline: auto; }

/* ----------------------------------------------------------------- forms */

/* The studio's own forms (the enquiry form on /contact). The shop's Bootstrap
   forms are skinned separately in studio-shop.css. */
.field { margin: 0 0 var(--s-5); }
.field > label {
    display: block;
    font-size: var(--t-xs);
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--slate-soft);
    margin-bottom: var(--s-2);
}
.field input,
.field select,
.field textarea {
    width: 100%;
    padding: 11px 13px;
    border: 1px solid var(--line-control);
    border-radius: var(--radius);
    background: var(--paper);
    color: var(--ink);
    font-family: var(--body);
    font-size: var(--t-base);
    line-height: 1.4;
}
.field textarea { resize: vertical; min-height: 8rem; }
.field input:focus,
.field select:focus,
.field textarea:focus {
    border-color: var(--action);
    outline: 2px solid var(--action);
    outline-offset: -2px;
}
.field--pair { display: grid; gap: var(--s-4); grid-template-columns: repeat(auto-fit, minmax(min(12rem, 100%), 1fr)); }
.formnote { font-size: var(--t-xs); color: var(--slate-soft); margin: var(--s-4) 0 0; }

/* A form or panel that reports success or failure. */
.notice { border-left: 4px solid var(--wall-deep); }
.notice--ok { border-left-color: var(--ok); }
.notice--bad { border-left-color: var(--alert); }

/* The small print under a table or a figure. One class instead of six copies
   of the same inline style. */
.fineprint { font-size: var(--t-sm); color: var(--slate); margin-top: var(--s-4); max-width: var(--measure-wide); }

/* ------------------------------------------------------- order builder */

/*
 * The order builder PROTOTYPE. Preview on the left, five numbered steps on the
 * right, and a pinned bar along the bottom that always shows the price and
 * what is blocking the order. Shape only: nothing here takes a file and
 * nothing here takes money. See templates/studio-order.php.
 */

.builder { display: grid; gap: var(--s-6); grid-template-columns: 1fr; align-items: start; }
@media (min-width: 62rem) { .builder { grid-template-columns: 1fr 1fr; } }

.builder__preview {
    position: sticky;
    top: 92px;
    background: var(--wall);
    border: 1px solid var(--wall-deep);
    border-radius: var(--radius);
    aspect-ratio: 4 / 3;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--s-3);
    text-align: center;
    padding: var(--s-6);
    color: var(--slate);
}
.builder__preview strong { font-family: var(--display); font-size: var(--t-md); color: var(--ink); }
.builder__preview p { font-size: var(--t-sm); margin: 0; max-width: 22rem; }
@media (max-width: 62rem) { .builder__preview { position: static; } }

.builder__steps { display: grid; gap: var(--s-4); counter-reset: bstep; }

.bstep {
    background: var(--paper);
    border: 1px solid var(--wall-deep);
    border-radius: var(--radius);
    padding: var(--s-5);
    counter-increment: bstep;
}
.bstep--todo { border-style: dashed; background: transparent; }
.bstep__head { display: flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-4); }
.bstep__head::before {
    content: counter(bstep);
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem; height: 1.75rem;
    border-radius: 50%;
    background: var(--action);
    color: var(--paper);
    font-family: var(--display);
    font-weight: 700;
    font-size: var(--t-sm);
}
.bstep--todo .bstep__head::before { background: var(--slate-soft); color: var(--paper); }
.bstep__head h3 { margin: 0; font-size: var(--t-md); }
.bstep__state { margin-left: auto; font-size: var(--t-xs); font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--slate-soft); }
.bstep__state--set { color: var(--ok); }

/* The chip row: a size, a paper, a finish. One is always selected. */
.chips { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.chip {
    font-family: var(--body);
    font-size: var(--t-sm);
    font-weight: 500;
    padding: 8px 14px;
    border: 1px solid var(--line-control);
    border-radius: var(--radius);
    background: var(--paper);
    color: var(--ink);
    cursor: pointer;
    line-height: 1.2;
}
.chip:hover { border-color: var(--ink); }
.chip[aria-pressed="true"] { border-color: var(--action); background: rgba(var(--action-rgb), .07); color: var(--action-ink); font-weight: 700; }
.chip small { display: block; font-weight: 400; font-size: var(--t-xs); color: var(--slate-soft); margin-top: 2px; }
.chip[aria-pressed="true"] small { color: var(--action-ink); }

/* The pinned bar. Never grey: when it cannot proceed it says why. */
.orderbar {
    position: sticky;
    bottom: 0;
    z-index: 35;
    background: var(--paper);
    border-top: 1px solid var(--wall-deep);
    box-shadow: 0 -2px 12px rgba(30,27,24,.08);
}
.orderbar__in {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-4);
    padding-block: var(--s-3);
}
.orderbar__left { font-size: var(--t-sm); color: var(--slate); }
.orderbar__left strong { color: var(--ink); }
.orderbar__price {
    margin-left: auto;
    font-family: var(--display);
    font-weight: 700;
    font-size: var(--t-xl);
    font-variant-numeric: tabular-nums;
}
.orderbar__price small { display: block; font-family: var(--body); font-weight: 400; font-size: var(--t-xs); color: var(--slate-soft); }

.stepper { display: inline-flex; align-items: stretch; border: 1px solid var(--line-control); border-radius: var(--radius); overflow: hidden; }
.stepper button {
    width: 2.25rem;
    border: 0;
    background: var(--paper);
    color: var(--ink);
    font-size: var(--t-md);
    line-height: 1;
    cursor: pointer;
}
.stepper button:hover { background: var(--wall); }
.stepper input {
    width: 3rem;
    border: 0;
    border-inline: 1px solid var(--line-control);
    text-align: center;
    font-family: var(--body);
    font-size: var(--t-base);
    color: var(--ink);
    background: var(--paper);
    -moz-appearance: textfield;
    appearance: textfield;
}
.stepper input::-webkit-outer-spin-button,
.stepper input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

@media (max-width: 40rem) {
    .orderbar__price { margin-left: 0; }
    .orderbar__in > .btn { width: 100%; }
}

/* Prototype banner. Anything that looks real but is not wired says so. */
.protoflag {
    background: var(--cream);
    border: 1px dashed var(--cream-line);
    border-radius: var(--radius);
    color: var(--cream-ink);
    font-size: var(--t-sm);
    padding: var(--s-3) var(--s-4);
    margin-bottom: var(--s-5);
    max-width: var(--measure-wide);
}
.protoflag strong { font-weight: 700; }

@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *, *::before, *::after {
        animation-duration: .001ms !important;
        transition-duration: .001ms !important;
    }
}

/* ------------------------------------------------------------------------
   Specificity restatements.

   The CMS ships plugin stylesheets that define their own .btn and colour bare
   anchors with their own ink. studio.css already loads last, but several of
   those rules carry more than one class of specificity, so the theme states
   these again, scoped with .studio, and wins on specificity rather than on
   order. Found by driving the pages in Chrome, 2026-09-03; kept through the
   2026-09-04 rewrite because the plugin sheets have not changed.
   ------------------------------------------------------------------------ */

.studio .btn { white-space: nowrap; line-height: 1.2; }

.studio a.btn--primary, .studio button.btn--primary,
.studio a.btn--brass, .studio button.btn--brass {
    background: var(--action); color: var(--paper); border-color: var(--action);
}
.studio a.btn--primary:hover, .studio button.btn--primary:hover,
.studio a.btn--brass:hover, .studio button.btn--brass:hover {
    background: var(--action-dark); color: var(--paper); border-color: var(--action-dark);
}
.studio a.btn--ghost, .studio button.btn--ghost { background: transparent; color: var(--ink); border-color: var(--line-soft); }
.studio a.btn--ghost:hover, .studio button.btn--ghost:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.studio .band--ink a.btn--ghost { color: var(--paper); border-color: rgba(255,255,255,.55); }
.studio .band--ink a.btn--ghost:hover { background: var(--paper); color: var(--deep); border-color: var(--paper); }
.studio a.btn--text, .studio button.btn--text { background: transparent; border-color: transparent; color: var(--ink); }
.studio .band--ink a.btn--text { color: var(--paper); }

/* Plugin sheets set a display on bare anchors; the card and surface tiles need
   their own. */
.studio a.card { display: flex; }
.studio a.surface { display: flex; }

/* Tables inside plugin CSS pick up borders we do not want. */
.studio .ptable, .studio .ptable td, .studio .ptable th { border-left: 0; border-right: 0; }

.studio .site-head__utility .btn { padding: 9px 16px; align-self: center; font-size: var(--t-sm); }

/* ----------------------------------------------------------- testimonials */

/*
 * A review card. Deliberately quiet: no avatar circles, no company logos, no
 * five gold stars shouting across the page. The words are the proof, so the
 * words get the size and everything else gets out of the way.
 *
 * Used on /reviews, on the home page, and on any service page with reviews
 * against that service.
 */

.quote {
    display: flex;
    flex-direction: column;
    gap: var(--s-3);
    margin: 0;
}

.quote__stars {
    color: var(--action);
    font-size: var(--t-sm);
    letter-spacing: .12em;
    line-height: 1;
}

.quote blockquote {
    margin: 0;
    font-family: var(--display);
    font-size: 1.0625rem;
    line-height: 1.55;
    color: var(--ink);
}

.quote blockquote::before { content: '\201C'; }
.quote blockquote::after  { content: '\201D'; }

.quote figcaption {
    margin-top: auto;
    padding-top: var(--s-3);
    border-top: 1px solid var(--wall-deep);
    font-size: var(--t-sm);
    color: var(--slate);
}

.quote figcaption strong { display: block; color: var(--ink); }
.quote figcaption span   { color: var(--slate-soft); }

/* ---------------------------------------------- the builder, working parts */

/*
 * Added 2026-09-09 when the order builder stopped being a prototype: the drop
 * zone, the per-size resolution badges, the custom size row, and the step
 * messages that carry a verdict.
 *
 * The verdict colours are the only place on this site where green and amber
 * appear as meaning rather than as decoration. That is deliberate: a customer
 * deciding how big to print is the one moment where a traffic light is the
 * clearest thing we can show them.
 */

.dropzone {
    display: flex;
    flex-direction: column;
    gap: 4px;
    align-items: center;
    text-align: center;
    padding: var(--s-6) var(--s-5);
    border: 1px dashed var(--line-soft);
    border-radius: var(--radius);
    background: var(--ground);
    cursor: pointer;
    transition: border-color .15s ease, background-color .15s ease;
}
.dropzone:hover, .dropzone.is-over { border-color: var(--action); background: rgba(var(--action-rgb), .04); }
.dropzone__main { font-weight: 700; color: var(--ink); }
.dropzone__sub  { font-size: var(--t-xs); color: var(--slate-soft); max-width: 26rem; }

.bstep__msg {
    margin: var(--s-4) 0 0;
    font-size: var(--t-sm);
    color: var(--slate);
    line-height: 1.5;
}
.bstep__msg--ok        { color: var(--ok); }
.bstep__msg--bad       { color: var(--alert); }
.bstep__msg--excellent { color: var(--ok); }
.bstep__msg--good      { color: var(--ok); }
.bstep__msg--fair      { color: var(--slate); }
.bstep__msg--poor      { color: var(--alert); }
.bstep__msg--too-small { color: var(--alert); font-weight: 700; }

/* The badge on each size chip. Border weight carries the verdict as well as
   colour, so it still reads without colour vision. */
.chip[data-verdict="excellent"],
.chip[data-verdict="good"]      { border-color: var(--ok); }
.chip[data-verdict="excellent"] small,
.chip[data-verdict="good"]      small { color: var(--ok); }
.chip[data-verdict="fair"]      small { color: var(--slate); }
.chip[data-verdict="poor"]      { border-color: var(--alert); }
.chip[data-verdict="poor"]      small { color: var(--alert); }
.chip[data-verdict="too-small"] { border-color: var(--alert); border-style: dashed; opacity: .75; }
.chip[data-verdict="too-small"] small { color: var(--alert); }

.customsize {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-3);
    margin-top: var(--s-4);
    padding-top: var(--s-4);
    border-top: 1px solid var(--wall-deep);
}
.customsize__label  { font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .06em; color: var(--slate-soft); }
.customsize__fields { display: flex; align-items: center; gap: 6px; }
.customsize input {
    width: 5rem;
    padding: 8px 10px;
    border: 1px solid var(--line-soft);
    border-radius: var(--radius);
    font: inherit;
    font-size: var(--t-sm);
}

/* The uploaded image itself, sitting in the preview panel. */
.builder__preview figure { margin: 0; display: flex; flex-direction: column; gap: var(--s-3); align-items: center; max-width: 100%; }
.builder__preview img {
    max-width: 100%;
    max-height: 22rem;
    object-fit: contain;
    border: 1px solid var(--wall-deep);
    background: var(--paper);
    box-shadow: var(--lift-1);
}
.builder__preview figcaption { font-size: var(--t-xs); color: var(--slate-soft); word-break: break-all; }

.sr-only {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* ------------------------------------------- theme vs plugin, one exception */

/*
 * `.studio img { height: auto }` above is right for content images and wrong
 * for a plugin that sizes an image with an HTML height attribute. An author
 * stylesheet beats a presentational attribute, so the shop's 18px carrier
 * logos rendered at their natural size and swamped the shipping options.
 * Found in Chrome on 2026-09-09 while buying a print.
 *
 * Scoped to the two places a plugin does this rather than reverted globally,
 * because the global rule is what stops a pasted image breaking a page.
 */
.studio .carrier-icon img,
.studio .shipping-rate-option img,
.studio #shipping-rates img {
    height: 18px;
    width: auto;
    display: inline-block;
}

/* ---------------------------------------------------------- work samples */

/*
 * The grid that carries photographs of the work. Sized so a row of six reads
 * as a portfolio rather than as a product listing: no borders, no shadows, no
 * hover lift. The picture is the thing.
 */

.samples {
    display: grid;
    gap: var(--s-5);
    grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
}

/* Three across at desk width. auto-fit gave four, which left the last two
   stranded beside a hole, and bigger pictures are the point of this section. */
@media (min-width: 68rem) {
    .samples { grid-template-columns: repeat(3, 1fr); gap: var(--s-6); }
}

.sample { margin: 0; }

.sample img {
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
    background: var(--wall);
    border: 1px solid var(--wall-deep);
}

.sample figcaption {
    margin-top: var(--s-3);
    font-size: var(--t-sm);
    line-height: 1.5;
    color: var(--slate);
}

.sample figcaption strong { display: block; color: var(--ink); font-family: var(--display); }

/* The hero image now carries a credit line, because the placeholder in it is
   somebody else's public domain work and saying so is cheaper than being asked. */
.studio .hero__figure { margin: 0; display: flex; flex-direction: column; gap: var(--s-3); }
.studio .hero__figure figcaption {
    font-size: var(--t-xs);
    line-height: 1.5;
    color: var(--slate-soft);
}

/* ------------------------------------------------- the ask, beside a table */

/*
 * The 2026-09-08 review found /pricing carried exactly one call to action, in
 * its last sentence, at word 496 of 502, on the highest-intent page on the
 * site. Every price table now ends with the one action that fits what it
 * priced. Quiet by design: a rule, a sentence and a button, not a banner.
 */

.tablecta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-4);
    margin-top: var(--s-5);
    padding-top: var(--s-5);
    border-top: 1px solid var(--wall-deep);
}

.tablecta p {
    margin: 0;
    font-size: var(--t-sm);
    color: var(--slate);
    max-width: 34rem;
}

/* ------------------------------------------------ the five step process row */

/*
 * auto-fit with a 16rem minimum gives four columns at around 1450px, which
 * strands the fifth card alone beside a wide empty space. Found in the browser
 * on 2026-09-08 and again on 2026-09-09.
 *
 * A five item list is laid out explicitly instead: five across when there is
 * genuinely room, three then two when there is not. Lists of other lengths keep
 * the auto-fit behaviour, which is right for them.
 */

@media (min-width: 68rem) {
    .steps:has(li:nth-child(5)):not(:has(li:nth-child(6))) {
        grid-template-columns: repeat(5, 1fr);
        gap: var(--s-5);
    }
}

@media (min-width: 46rem) and (max-width: 67.999rem) {
    .steps:has(li:nth-child(5)):not(:has(li:nth-child(6))) {
        grid-template-columns: repeat(3, 1fr);
    }
}

/* ============================================================== MOTION ==== */

/*
 * Rules this whole block obeys, from the 2026-09-08 plan section 6:
 *
 *   1. transform and opacity only. Never top, height, margin or width.
 *      Scroll input does not get the 500ms input-exclusion grace period a
 *      click gets, so a layout shift caused by a scroll animation counts in
 *      full against Cumulative Layout Shift, and Lighthouse cannot see it
 *      because Lighthouse does not scroll.
 *   2. Nothing delays first paint. Every element is visible with JavaScript
 *      off, and the reveal class is only added once the observer has run.
 *   3. prefers-reduced-motion is honoured everywhere, no exceptions.
 *
 * And the honest bit, from the same plan: there is NO measured evidence that
 * any of this sells more prints. It is here because a considered site reads as
 * a considered lab. The one thing with real numbers behind it is page speed,
 * which is why none of it costs any.
 */

/* --- reveal on entry ---------------------------------------------------- */

/* No JS, no class, no change: the element is simply visible. */
.reveal {
    opacity: 0;
    transform: translateY(12px);
    transition: opacity .45s cubic-bezier(.2, .6, .3, 1), transform .45s cubic-bezier(.2, .6, .3, 1);
    will-change: opacity, transform;
}

.reveal.is-in {
    opacity: 1;
    transform: none;
}

/* Children stagger slightly, so a grid arrives as a group rather than a wall. */
.reveal--stagger > * {
    opacity: 0;
    transform: translateY(10px);
    transition: opacity .4s ease, transform .4s ease;
}
.reveal--stagger.is-in > *              { opacity: 1; transform: none; }
.reveal--stagger.is-in > *:nth-child(2) { transition-delay: .06s; }
.reveal--stagger.is-in > *:nth-child(3) { transition-delay: .12s; }
.reveal--stagger.is-in > *:nth-child(4) { transition-delay: .18s; }
.reveal--stagger.is-in > *:nth-child(5) { transition-delay: .24s; }
.reveal--stagger.is-in > *:nth-child(6) { transition-delay: .30s; }

/* --- hover lift --------------------------------------------------------- */

.card, .paper, .sample img, .surface {
    transition: transform .25s ease, box-shadow .25s ease;
}

@media (hover: hover) and (pointer: fine) {
    .card:hover, a.card:hover { transform: translateY(-3px); box-shadow: var(--lift-2); }
    .sample:hover img         { transform: scale(1.015); }
    .surface:hover            { transform: translateY(-2px); }
}

.sample img { transition: transform .35s ease; }

/* --- the surfaces, on hover --------------------------------------------- */

/* The swatch grows inside its own box rather than pushing anything, which is
   the difference between a texture zoom and a layout shift. */
.surface__swatch { transition: transform .3s ease; transform-origin: center; }

@media (hover: hover) and (pointer: fine) {
    .surface:hover .surface__swatch { transform: scale(1.06); }
}

/* --- the price, when it changes ----------------------------------------- */

@keyframes pricepulse {
    0%   { transform: scale(1); }
    35%  { transform: scale(1.06); }
    100% { transform: scale(1); }
}

.orderbar__price.is-changed > [data-b="total"] {
    display: inline-block;
    animation: pricepulse .32s ease;
}

/* --- and the opt out ----------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
    .reveal,
    .reveal--stagger > * {
        opacity: 1 !important;
        transform: none !important;
        transition: none !important;
    }
    .card, .paper, .sample img, .surface, .surface__swatch { transition: none !important; }
    .card:hover, a.card:hover, .sample:hover img, .surface:hover, .surface:hover .surface__swatch {
        transform: none !important;
    }
    .orderbar__price.is-changed > [data-b="total"] { animation: none !important; }
}

/* The nudge under the builder steps. Quiet: it states a fact about postage or
   names the next volume break, and it is never a banner. */
.nudge {
    margin: 0;
    padding: var(--s-4) var(--s-5);
    border: 1px solid var(--wall-deep);
    border-left: 3px solid var(--action);
    border-radius: var(--radius);
    background: var(--paper);
    font-size: var(--t-sm);
    line-height: 1.55;
    color: var(--slate);
}
.nudge strong { color: var(--ink); }

/* ==========================================================================
   THE 2026-09-10 SALES REWRITE
   Components for pages that sell rather than describe. Rules in
   notes/SALES_COPY_STANDARDS.md. Every one of these is built to carry an
   image, because a print business that shows no pictures has already lost.
   ========================================================================== */

/* The home hero is taller than a service hero and darker, because a photograph
   is doing the arguing and the type has to sit on top of it. */
.hero--home { min-height: 34rem; }
.hero--home h1 { font-size: clamp(2.25rem, 5.2vw, 3.5rem); line-height: 1.06; }
@media (max-width: 48rem) {
    .hero--home { min-height: 0; }
}

/* --------------------------------------------------------- the three doors

   One card per audience. The whole card is the link: a customer should never
   have to find a small target. */

.doorcards {
    display: grid;
    gap: var(--s-5);
    grid-template-columns: repeat(auto-fit, minmax(min(19rem, 100%), 1fr));
    margin-top: var(--s-6);
}
.studio a.doorcard {
    display: flex;
    flex-direction: column;
    background: var(--paper);
    border: 1px solid var(--wall-deep);
    border-radius: var(--radius);
    overflow: hidden;
    text-decoration: none;
    color: var(--ink);
    transition: box-shadow var(--speed) var(--ease),
                border-color var(--speed) var(--ease),
                transform var(--speed) var(--ease);
}
.studio a.doorcard:hover {
    border-color: var(--action);
    box-shadow: var(--lift-2);
    transform: translateY(-3px);
}
.studio a.doorcard img {
    display: block;
    width: 100%;
    aspect-ratio: 3 / 2;
    object-fit: cover;
    transition: transform 1.1s var(--ease);
}
.studio a.doorcard:hover img { transform: scale(1.04); }
.doorcard__body {
    display: flex;
    flex-direction: column;
    gap: var(--s-2);
    padding: var(--s-5);
    flex: 1 1 auto;
}
.doorcard__body strong {
    font-family: var(--display);
    font-size: var(--t-xl);
    line-height: 1.15;
}
.doorcard__body > span:not([class]) { color: var(--slate); font-size: var(--t-sm); }
.doorcard__price {
    font-family: var(--display);
    font-weight: 700;
    color: var(--action);
    font-size: var(--t-lg);
    margin-top: auto;
    padding-top: var(--s-3);
}
.doorcard__go {
    font-size: var(--t-xs);
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--action);
}
.studio a.doorcard:hover .doorcard__go { text-decoration: underline; }

/* ------------------------------------------------------------- showcase

   Proof. Real work, one wide and two beside it, collapsing to a stack. */

.showcase {
    display: grid;
    gap: var(--s-4);
    grid-template-columns: repeat(2, 1fr);
    margin-top: var(--s-6);
}
@media (min-width: 62rem) {
    .showcase { grid-template-columns: repeat(4, 1fr); }
    .showcase__item--wide { grid-column: span 2; grid-row: span 2; }
}
@media (max-width: 40rem) {
    .showcase { grid-template-columns: 1fr; }
}
.studio .showcase__item { margin: 0; position: relative; overflow: hidden; border-radius: var(--radius); }
.studio .showcase__item img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 1.2s var(--ease);
}
.studio .showcase__item:hover img { transform: scale(1.05); }
.studio .showcase__item figcaption {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    padding: var(--s-5) var(--s-4) var(--s-3);
    color: var(--paper);
    font-size: var(--t-xs);
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    background: linear-gradient(180deg, transparent, rgba(20,16,14,.78));
}

/* -------------------------------------------------------------- reasons */

.reasons {
    display: grid;
    gap: var(--s-6);
    grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
    margin-top: var(--s-6);
}
.reason__num {
    display: block;
    font-family: var(--display);
    font-size: var(--t-2xl);
    font-weight: 700;
    color: var(--action);
    opacity: .3;
    line-height: 1;
    margin-bottom: var(--s-2);
}
.studio .reason h3 { font-size: var(--t-md); margin: 0 0 var(--s-2); }
.studio .reason p { color: var(--slate); font-size: var(--t-sm); margin: 0; }

/* ------------------------------------------------------- the closing band

   A photograph with the last call to action on top of it. Same scrim logic as
   the full-bleed hero, so white type always clears AA. */

.band--cta {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    background: var(--ink);
    color: var(--paper);
    text-align: center;
}
.studio .band--cta .band__bg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    z-index: -2;
}
.band--cta::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: linear-gradient(180deg, rgba(20,16,14,.84), rgba(20,16,14,.90));
}
.studio .band--cta h2 { color: var(--paper); }
.studio .band--cta p { color: rgba(255,255,255,.88); max-width: 40rem; margin-inline: auto; }
.band__cta-in .doors { justify-content: center; margin-top: var(--s-5); }
.studio .band--cta .btn--ghost { color: var(--paper); border-color: rgba(255,255,255,.55); }
.studio .band--cta .btn--ghost:hover { background: var(--paper); color: var(--ink); border-color: var(--paper); }

/* The home hero's three doors need one line to themselves at desktop: three
   buttons wrapping 2+1 reads as a mistake rather than a menu. */
@media (min-width: 62rem) {
    .hero--home .hero__in { grid-template-columns: minmax(0, 44rem) 1fr; }
}
.hero--home .doors--three { margin-top: var(--s-5); }

/* The showcase's wide item spans two rows, so the rows have to be a fixed
   height or the tall item collapses to the height of the short ones beside it. */
@media (min-width: 62rem) {
    .showcase { grid-auto-rows: 13rem; }
    .studio .showcase__item--wide img { object-position: center 40%; }
}
@media (min-width: 40rem) and (max-width: 61.99rem) {
    .showcase { grid-auto-rows: 11rem; }
    .showcase__item--wide { grid-column: span 2; }
}
@media (max-width: 39.99rem) {
    .studio .showcase__item { aspect-ratio: 3 / 2; }
}

/* Every page ends with the action AND the invitation to just ask. */
.closing__aside { margin-top: var(--s-4); font-size: var(--t-sm); }
.studio .band--ink .closing__aside { color: rgba(255,255,255,.78); }
.studio .band--ink .closing__aside a { color: var(--paper); text-decoration-color: rgba(255,255,255,.6); }
/* Shared sage accent, drawn from the mentoring workshop card. */
.studio { --studio-sage:#e8eddf; }
.studio .site-head .site-head__inquiry {
  background:var(--studio-sage);
  color:#344d3e;
  padding:9px 12px;
  border-radius:2px;
  text-decoration:none;
}
.studio .site-head .site-head__inquiry:hover {
  text-decoration:underline;
  text-underline-offset:4px;
}
.studio .site-foot { border-top:6px solid var(--studio-sage); }
.studio .atelier-section-label::before {
  content:'';
  display:inline-block;
  width:22px;
  height:5px;
  margin-right:10px;
  vertical-align:middle;
  background:var(--studio-sage);
}
/* One substantial sage surface per public page, in addition to shared accents.
   Mentoring already uses this color on its workshop card. Keep selectors
   scoped to each page so a second card or section cannot pick it up. */
.studio.v2-home #main > .v2-review,
.studio.v2-printing #main .print-surface--help,
.studio:is(.v2-restoration,.v2-editing,.v2-art-reproduction,.v2-scanning-preservation) #main > #section-2,
.studio.page-pricing #main .price-help,
.studio.page-contact #main .split > aside > .paper:first-child,
.studio.page-order #main .order-care,
.studio.personal-service--photography #main .personal-plan,
.studio.page-reviews #main > .v2-closing,
.studio.page-policy #main > #production-errors,
.studio.page-plain #main > :nth-child(1 of .band--ground),
.studio.page-service #main > :nth-child(1 of .band--ground),
.studio.ecommerce-shop.page-login #main .card.shadow-sm,
.studio.ecommerce-shop:not(.page-login) #main > .container > nav:first-child {
  background:var(--studio-sage);
}
.studio.page-pricing #main .price-help {
  padding:28px;
  border:1px solid #cbd5c2;
  border-radius:6px;
}
.studio.v2-printing #main .print-surface--help,
.studio.v2-printing #main .print-surface--help :is(h3,p,.eyebrow) { color:#344d3e; }
/* The shop skin marks card backgrounds important; limit this override to login. */
.studio.ecommerce-shop.page-login #main .card.shadow-sm { background:var(--studio-sage) !important; }
.studio.ecommerce-shop:not(.page-login) #main > .container > nav:first-child {
  padding:12px 16px;
  border-radius:2px;
}

/* Keep the complete legal page readable when printed for review. */
@media print {
  body.studio.page-policy { background:#fff !important; color:#000 !important; font:10pt/1.45 Georgia,serif; }
  .studio.page-policy .v2-hero::before,
  .studio.page-policy .v2-hero::after { display:none !important; }
  .studio.page-policy .promobar,
  .studio.page-policy .site-head,
  .studio.page-policy .site-foot,
  .studio.page-policy .v2-hero p:last-child { display:none !important; }
  .studio.page-policy #main,
  .studio.page-policy #main section,
  .studio.page-policy #main .wrap { width:auto; max-width:none; margin:0; padding:0; background:#fff !important; color:#000 !important; overflow:visible; }
  .studio.page-policy #main .v2-section { padding-top:12pt; }
  .studio.page-policy #main h1 { font-size:20pt; }
  .studio.page-policy #main h2 { font-size:13pt; margin:14pt 0 6pt; break-after:avoid; page-break-after:avoid; }
  .studio.page-policy #main p { font:10pt/1.45 Georgia,serif; margin:0 0 8pt; orphans:3; widows:3; }
  .studio.page-policy #main :is(h1,h2,p,a) { color:#000 !important; text-shadow:none; }
  .studio.page-policy #main a { text-decoration:underline; }
}
