/* DS Mini Storage -- site design. Run D1, 22 Aug 2026. Homepage only;
 * D2 carries this system to the other seven pages.
 *
 * Per the factory standard rule 8: the template ships no design, so
 * this file is where DS Mini Storage's own visual identity actually
 * lives. Builds on tokens.css (values) and structure.css (structural
 * floors this file never overrides -- focus rings, reduced-motion,
 * the 16px body floor). Every colour reference below is a var().
 *
 * ANTI-CLICHE PASS, stated here rather than only in the run file so
 * the refusal is visible next to the code that enacts it:
 *
 * Expected stack for a site like this: hero -> stat bar -> problem ->
 * cards -> CTA.
 *
 * REFUSED: the stat bar. No "20+ years", "500+ happy customers" row --
 * brand.md forbids implying scale this business hasn't confirmed, and
 * the factory standard forbids inventing a statistic outright. There
 * is nothing to put in that row that would be true.
 *
 * REFUSED: identical icon cards for the routing links. The expected
 * move is four uniform bordered boxes, each with a generic icon --
 * the routing links below vary in weight and are typography-led, not
 * icon-led, because this business has no icon set and inventing one
 * would be exactly the "generated" look the standard exists to catch.
 *
 * KEPT, deliberately: no dramatized "problem" section either (a third,
 * unforced refusal) -- icp.md is explicit that this reader should
 * never feel rushed or agitated into a decision; the site states facts
 * and lets the reader's own situation supply the urgency or not.
 */

/* =====================================================================
   HEADER / NAV -- navy, bookending the page with the CTA section below.
   ===================================================================== */

.site-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  background: var(--color-navy);
  /* Local focus-ring override: the global --color-focus (navy) would be
     invisible against this navy background. Verified 9.84:1 white on
     navy -- same pairing already proven for --color-navy-text. */
  --color-focus: var(--color-focus-on-navy);
}

.site-name {
  display: inline-flex;
  align-items: center;
}

.site-name img {
  /* Raw attributes on the <img> are the real 302x200 pixels, to hold
     layout before the stylesheet loads. Display size is a design
     decision, made here, not in the markup. */
  max-height: 2.75rem;
  width: auto;
  height: auto;
}

.site-nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
  list-style: none;
}

.site-nav a {
  display: inline-flex;
  align-items: center;
  /* 44px tap-target floor, guaranteed regardless of font metrics --
     padding alone measured 39px live (mobile check, run D1), short of
     the floor. min-height plus flex centring holds the floor exactly,
     not approximately. */
  min-height: 44px;
  color: var(--color-navy-text);
  text-decoration: none;
  font-weight: 500;
  border-bottom: 2px solid transparent;
  transition: border-color var(--transition-fast);
}

.site-nav a:hover,
.site-nav a[aria-current="page"] {
  border-bottom-color: var(--color-accent);
}

.site-phone {
  color: var(--color-navy-text);
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
}

.site-phone:hover { color: var(--color-accent); }

/* =====================================================================
   HEADINGS -- split-heading is the one eyebrow device on this page
   (design rule 2). The small half is genuinely part of the sentence,
   set smaller and in the muted colour so the large half reads as the
   point and the small half reads as its lead-in, not a label sitting
   above it.
   ===================================================================== */

h1, h2, h3, h4 {
  font-weight: var(--font-weight-heading);
  color: var(--color-navy);
}

.split-heading {
  font-size: var(--font-size-2xl);
  letter-spacing: var(--letter-spacing-heading);
}

.split-heading-lead {
  display: block;
  font-size: var(--font-size-md);
  font-weight: 500;
  color: var(--color-text-muted);
  letter-spacing: normal;
  margin-bottom: 0.15em;
}

.split-heading-main {
  display: block;
  color: var(--color-navy);
}

.answer-question {
  font-size: var(--font-size-xl);
  margin-bottom: var(--space-xs);
}

h3.answer-question {
  font-size: var(--font-size-lg);
}

.lede {
  font-size: var(--font-size-md);
  color: var(--color-text-muted);
  margin-top: var(--space-sm);
}

.last-updated {
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
  margin-top: var(--space-xs);
}

/* =====================================================================
   CONTENT IMAGES -- the plain <figure class="content-image"> used to
   drop a real photo into the body of an answer/question section (D2,
   23 Aug 2026: units, faq, contact, areas/lawson). Deliberately the
   same treatment everywhere rather than a per-page variant, so a
   photo mid-page reads as one consistent device, not four one-offs.
   ===================================================================== */

.content-image {
  margin-block: var(--space-lg);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow-md);
}

.content-image img {
  display: block;
  width: 100%;
  height: auto;
}

/* =====================================================================
   BUTTONS
   ===================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  padding: 0.85em 1.5em;
  border-radius: var(--radius);
  font-weight: 700;
  text-decoration: none;
  /* 44px tap-target floor (mobile requirement) regardless of content. */
  min-height: 44px;
  transition: transform var(--transition-fast), box-shadow var(--transition-fast),
              background-color var(--transition-fast);
}

.btn-primary {
  background: var(--color-accent);
  color: var(--color-accent-text);
  box-shadow: var(--shadow-sm);
}

.btn-primary:hover {
  background: var(--color-accent-dark);
  box-shadow: var(--shadow-md);
}

.btn-lg {
  padding: 1em 2em;
  font-size: var(--font-size-md);
}

/* Button Nudge: a small, slow, looping horizontal nudge on the primary
   CTA -- restrained on purpose (short travel, long pause between
   nudges, transform only). One attention-drawer, this section. */
@keyframes btn-nudge {
  0%, 88%, 100% { transform: translateX(0); }
  92% { transform: translateX(3px); }
  96% { transform: translateX(-1px); }
}

.btn-primary.btn-nudge {
  animation: btn-nudge 4s ease-in-out infinite;
}

.btn-primary.btn-nudge:hover,
.btn-primary.btn-nudge:focus-visible {
  animation-play-state: paused;
}

/* =====================================================================
   SECTIONS -- base rhythm, then per-section variation so no two share
   an exact structure (design rule 1). Seven sections on this page,
   seven different shapes: plain-text opening, callout panel (cost),
   two-column-with-image (where-when), compact-centred (availability),
   full-bleed image (proof), varied-weight link row (routing),
   navy-bookend-with-image (cta).
   ===================================================================== */

.section { padding-block: var(--space-xl); }

.opening { padding-top: var(--space-xl); }

/* -- #opening / .hero: D1b, run 23 Aug 2026. Background treatment
   only -- the heading and lede are exactly what B2 wrote, now sitting
   over a real photograph instead of a flat white background. Static:
   no Ken Burns, no parallax -- dazzle is already at its three-family
   ceiling (Scroll Reveal, Card Lift, Button Nudge) elsewhere on this
   page, and the restraint rule caps it there deliberately. Scrim
   opacity and object-position are both measured against the CURRENT
   hero image, not carried over from a previous one -- see the run
   file for the darkest/lightest contrast readings and the crop check.
   C2 swapped IMG_0276 for IMG_0277 here and re-derived both values
   from scratch against the new photo; neither number is assumed to
   transfer between photographs, because they don't -- IMG_0277's own
   re-measurement landed on a different object-position entirely. */

.hero {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  padding-block: var(--space-xl);
}

.hero-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* C2: re-derived for IMG_0277, replacing D1b's 78% (tuned for a
     different photo -- carrying it over was checked and it does NOT
     work for this image: at the wide desktop crop it shows road and
     grass with barely a sliver of vehicle, no doors at all). Compared
     0/30/50/65/78/100 by generating real crops at the actual hero-box
     size and looking; 30% shows the full roofline-to-ground height of
     the unit row plus the van, at every width where object-position
     has any effect (see the run file for why phone widths don't
     change here regardless of this value -- the crop is width-limited
     there, not height-limited). */
  object-position: center 30%;
  z-index: -2;
}

.hero-scrim {
  position: absolute;
  inset: 0;
  /* Flat opacity, not a gradient -- a gradient that passes at the top
     and fails over a bright patch of sky is the exact mistake the
     brief warns against. One measured value that holds everywhere is
     safer than a gradient tuned by eye.
     C2: re-measured against IMG_0277's own lightest/darkest regions
     (24x24px block-averaged, all four widths) rather than assumed to
     carry over from D1b's IMG_0276 numbers -- 0.72 was D1b's answer
     for a different photograph, not a fact about this scrim in
     general. It happens to still clear AA here too (worst case 6.39:1
     at 1265x352, full table in the run file), which is a genuine
     re-confirmation, not the same claim reused. */
  background: rgba(28, 43, 58, 0.72);
  z-index: -1;
}

.hero-content {
  position: relative;
}

.hero h1, .hero .split-heading-lead, .hero .split-heading-main,
.hero .lede, .hero .last-updated {
  /* White throughout, not the usual muted/navy split -- a photo's
     brightness varies in a way flat navy never did, so this run uses
     one text colour with the most contrast headroom rather than two
     colours that would need separately verifying against a variable
     background. */
  color: #ffffff;
}

/* -- #cost: the pricing answer, designed as the differentiator it is
   (decision O-3). No competitor in this market publishes a price at
   all -- this section gets the panel treatment the rest of the page
   does not, so a scanning reader's eye lands here. */

#cost .container {
  max-width: 48rem;
}

#cost .answer-unit {
  background: var(--color-surface);
  border-left: 4px solid var(--color-accent);
  border-radius: var(--radius);
  padding: var(--space-lg);
}

#cost .answer-question {
  font-size: var(--font-size-lg);
  color: var(--color-navy);
}

#cost .answer-block p {
  font-size: var(--font-size-md);
  line-height: var(--line-height-base);
}

#cost .answer-block { border-left: none; padding-left: 0; }

/* -- #where-when: text and the approach shot side by side once there
   is room. IMG_0276 answers "will I find it" beside the text that
   answers the same question in words -- images carrying the section,
   not decorating it (design rule 6). */

#where-when .container {
  display: grid;
  gap: var(--space-lg);
  align-items: start;
}

@media (min-width: 60em) {
  #where-when .container {
    grid-template-columns: 3fr 2fr;
  }
  #where-when .where-when-image { order: 2; }
  #where-when .where-when-text { order: 1; }
}

.where-when-image img {
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
}

/* -- #availability: deliberately compact and centred -- the shortest,
   plainest section on the page, on a tinted background so its brevity
   reads as a choice, not a gap. */

#availability {
  background: var(--color-surface);
  padding-block: var(--space-lg);
}

#availability .container {
  max-width: 40rem;
  text-align: center;
}

#availability .answer-block { border-left: none; padding-left: 0; }

/* -- #proof: THE rhythm break. Full-bleed, no heading rendered visually
   (the exit-condition comment is dev-only), the single big visual
   moment on the page. No added caption text -- the alt text already
   carries the description for anyone who needs it in words, and a
   wordless photograph is a complete, honest execution of this section
   rather than a gap needing invented prose. */

.rhythm-break {
  padding-block: var(--space-xl);
}

.rhythm-break .container {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--space-md);
}

/* Full-bleed photo exception specifically for #proof on homepage */
#proof.rhythm-break {
  padding-block: 0;
}

#proof.rhythm-break .container {
  max-width: none;
  padding-inline: 0;
}

.rhythm-break img {
  width: 100%;
  max-height: 70vh;
  object-fit: cover;
}

/* -- #routing: four links, varied weight rather than uniform icon
   cards (the anti-cliche refusal above). First link (units, the
   single-size answer) carries more visual weight since it answers the
   question this business's whole positioning rests on; the rest are
   plainer.

   SIGNATURE INTERACTION, D2 (Craft Standard WS-004 §6): the motif
   named in story.md §5 is the door you drive up to -- drive-up access,
   no corridor, no shared loading bay. A plain translateY lift on hover
   is the exact generic "Card Lift" the standard calls out as correct
   and forgettable on its own, so it is paired with a second detail
   that is not: an accent bar hidden below each link's bottom edge
   rises into place on hover/focus, the way a roll-up unit door does
   when you arrive at it, rather than sliding in from a side or fading
   in place. Transform only (translateY), same restraint rules as
   Button Nudge above -- no new effect family, this still reads as
   Card Lift, just aimed at this client's own detail instead of a
   generic float. */

#routing { background: var(--color-surface); }

#routing ul { gap: var(--space-md); }

#routing li a {
  position: relative;
  display: block;
  height: 100%;
  padding: var(--space-md);
  padding-bottom: calc(var(--space-md) + 4px);
  overflow: hidden;
  background: var(--color-bg);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius);
  text-decoration: none;
  font-weight: 600;
  color: var(--color-navy);
  box-shadow: var(--shadow-sm);
  transition: transform var(--transition-base), box-shadow var(--transition-base);
}

#routing li a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 4px;
  background: var(--color-accent);
  transform: translateY(100%);
  transition: transform var(--transition-base);
}

#routing li a:hover,
#routing li a:focus-visible {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lg);
}

#routing li a:hover::after,
#routing li a:focus-visible::after {
  transform: translateY(0);
}

#routing li:first-child a {
  font-size: var(--font-size-lg);
  background: var(--color-navy);
  color: var(--color-navy-text);
  --color-focus: var(--color-focus-on-navy);
}

/* -- #cta: bookends the page in navy, matching the header. The sign
   photo (IMG_0281) puts the same phone number the button states in a
   photograph of the real building, which is the whole trust move this
   business has that a template competitor cannot copy. */

#cta {
  background: var(--color-navy);
  color: var(--color-navy-text);
  --color-focus: var(--color-focus-on-navy);
}

#cta .container {
  display: grid;
  gap: var(--space-lg);
  align-items: center;
  text-align: center;
}

#cta h2 { color: var(--color-navy-text); }

#cta p { color: var(--color-navy-muted); }

/* :not(.btn) so a button inside #cta can never inherit this rule's
   white text -- found live, run D1: the first version of this button
   sat inside a <p>, #cta p a's specificity beat .btn-primary's, and
   the button rendered white-on-orange at 2.24:1, well under AA. Moved
   the button out of the <p> AND hardened the selector, so the same
   class of file cannot recur if a future edit re-nests it. */
#cta p a:not(.btn) { color: var(--color-navy-text); text-decoration: underline; }

#cta .cta-sign {
  justify-self: center;
  max-width: 20rem;
}

#cta .cta-sign img {
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
}

@media (min-width: 48em) {
  #cta .container { grid-template-columns: 2fr 1fr; text-align: left; }
  #cta .cta-sign { justify-self: end; }
}

/* =====================================================================
   ANSWER BLOCKS -- shared visual floor for every answer not already
   overridden by a section-specific panel above.
   ===================================================================== */

.answer-block p { color: var(--color-text); }

/* =====================================================================
   TESTIMONIAL -- C2, run 23 Aug 2026. One real, named quote
   (/pricing/, before the final CTA -- placement argued in that page's
   own comment and the C2 run file). Deliberately NOT a card-strip
   pattern -- there is exactly one of these, and a grid built to hold
   more would read as a gap rather than a choice.
   ===================================================================== */

.testimonial {
  max-width: 40rem;
  margin-inline: auto;
  padding: var(--space-lg);
  background: var(--color-surface);
  border-left: 4px solid var(--color-navy);
  border-radius: var(--radius);
  text-align: center;
}

.testimonial p {
  font-size: var(--font-size-lg);
  font-style: italic;
  color: var(--color-navy);
  max-width: none;
}

.testimonial p::before  { content: "\201C"; }
.testimonial p::after   { content: "\201D"; }

.testimonial footer {
  margin-top: var(--space-sm);
  font-size: var(--font-size-sm);
  font-style: normal;
  color: var(--color-text-muted);
}

/* =====================================================================
   FOOTER
   ===================================================================== */

.site-footer {
  background: var(--color-surface);
  padding: var(--space-lg) var(--space-md);
  margin-top: var(--space-xl);
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
}

.footer-name { font-weight: 700; color: var(--color-navy); }

.footer-nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin-top: var(--space-sm);
  list-style: none;
}

.footer-nav a { color: var(--color-text-muted); }

/* =====================================================================
   DAZZLE -- Scroll Reveal, per personality (local/trades/warm), per
   restraint rules: max one attention-drawer per viewport, 2-3 effect
   families total on the page (this page uses three: Scroll Reveal,
   Card Lift above, Button Nudge above), transform/opacity only,
   prefers-reduced-motion honoured globally (structure.css).

   Trail Glow and Tilt explicitly avoided -- too sci-fi for a family
   storage business (per the brief).

   PROGRESSIVE ENHANCEMENT, genuinely CSS-only: animation-timeline:
   view() is real scroll-driven CSS with no JavaScript, but it is not
   universally supported yet. Everything inside @supports starts
   visible with no transform -- a browser without support renders the
   page fully readable and simply skips the reveal, never hides
   content behind a script that might not run.
   ===================================================================== */

.section { opacity: 1; transform: none; }

@supports (animation-timeline: view()) {
  @keyframes reveal-rise {
    from { opacity: 0; transform: translateY(24px); }
    to   { opacity: 1; transform: translateY(0); }
  }

  .section:not(.rhythm-break) {
    animation: reveal-rise linear both;
    animation-timeline: view();
    animation-range: entry 0% cover 30%;
  }
}

/* =====================================================================
   TREATMENT B: COLOR-BLOCKED SYSTEM & UNIFIED HERO BANDS (Run D4)
   ===================================================================== */

.block-navy {
  background: var(--color-navy);
  color: #ffffff;
  padding: 3rem 0;
}

.block-navy h1,
.block-navy h2,
.block-navy h3,
.block-navy p,
.block-navy li,
.block-navy .lede {
  color: #ffffff;
}

#local-context .local-context-grid {
  display: grid;
  gap: var(--space-lg);
  align-items: start;
}

@media (min-width: 60em) {
  #local-context .local-context-grid {
    grid-template-columns: 1fr 1fr;
  }
}

.block-surface {
  background: var(--color-surface);
  padding: 3rem 0;
}

.block-white {
  background: #ffffff;
  padding: 3rem 0;
}

/* Hero Bands: Tall (Home) & Short (Interior) -- Hybrid B-with-photo (Run D4b) */
.hero-block-tall,
.hero-block-short {
  position: relative;
  background: var(--color-navy);
  color: #ffffff;
  overflow: hidden;
}

.hero-block-tall {
  padding: 4rem 0 3.5rem 0;
}

.hero-block-short {
  padding: 3rem 0 2.5rem 0;
}

.hero-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 30%;
  z-index: 1;
}

.hero-scrim {
  position: absolute;
  inset: 0;
  background: rgba(28, 43, 58, 0.80);
  z-index: 2;
}

.hero-block-tall .container,
.hero-block-short .container {
  position: relative;
  z-index: 3;
}

.hero-block-tall h1,
.hero-block-short h1,
.hero-block-tall .split-heading-lead,
.hero-block-short .split-heading-lead,
.hero-block-tall .split-heading-main,
.hero-block-short .split-heading-main,
.hero-block-tall .eyebrow,
.hero-block-short .eyebrow,
.hero-block-tall .last-updated,
.hero-block-short .last-updated,
.hero-block-tall .lede,
.hero-block-short .lede {
  color: #ffffff;
}

.hero-block-tall h1,
.hero-block-short h1 {
  margin-bottom: 0.5rem;
}

.hero-block-tall .lede,
.hero-block-short .lede {
  font-size: var(--font-size-md);
  margin-top: 0.75rem;
  max-width: 65ch;
}

/* Checkmark Benefit Band */
.checkmark-band {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin: 1.25rem 0;
}

.benefit-pill {
  background: var(--color-accent);
  color: var(--color-accent-text);
  padding: 0.45rem 1rem;
  border-radius: 30px;
  font-weight: 700;
  font-size: 0.95rem;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

/* Unit Card & Use Cards */
.unit-card-wrapper {
  background: #ffffff;
  color: var(--color-text);
  border-left: 8px solid var(--color-accent);
  border-radius: 8px;
  padding: 2rem;
  box-shadow: var(--shadow-lg);
  margin: 1.5rem 0;
}

.use-cards-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 1.5rem;
  margin: 1.5rem 0;
}

.use-card {
  background: var(--color-navy);
  color: #ffffff;
  border-radius: 8px;
  padding: 1.5rem;
}

.use-card h3,
.use-card p {
  color: #ffffff;
}

/* Towns Served Chips */
.town-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin: 1rem 0;
}

.chip {
  background: var(--color-navy);
  color: #ffffff;
  padding: 0.45rem 1.1rem;
  border-radius: 4px;
  font-weight: 700;
  text-decoration: none;
  display: inline-block;
  transition: transform var(--transition-fast), background var(--transition-fast);
}

.chip:hover {
  transform: translateY(-2px);
  background: var(--color-accent);
  color: var(--color-accent-text);
}

