/* ==========================================================================
   BH v2 — "Editorial Precision" redesign layer

   Loads after main.min.css + motion.css. Works within the existing HTML
   structure — same classes, elevated art direction:

     1. Tokens            type scale, tracking, color
     2. Typography        display hero, eyebrows, refined hierarchy
     3. Hero              full-viewport, huge title over the disc, scroll cue
     4. Section heads     eyebrow label + item count
     5. Work list         index numbers, full sweep hover, arrow
     6. Footer            giant CTA, live colour-only hover
     7. Details           selection, focus, scrollbar
   ========================================================================== */

:root {
  --v2-navy: #111927;
  --v2-ink: #0b1220;
  --v2-gray: #98a2b3;
  --v2-gray-dark: #667085;
  --v2-white: #fcfcfd;
  --v2-ease: cubic-bezier(0.16, 1, 0.3, 1);
  --v2-track: -0.032em;
}

/* ==========================================================================
   2. Typography
   ========================================================================== */

/* Display title — the hero statement. Hanken Grotesk is variable, so the
   weight sits between the named weights for a quieter authority than 700.

   Size is desktop-only (>=992px). Below that the title takes main.css's own
   ladder — 24px, then 36px from 768px — so the animated hero renders at the
   same size as the plain .page-header__title at tablet width instead of the
   display clamp's 48-85px (owner request, 2026-11-09). The clamp is kept for
   the desktop step it was written for.

   Leading is production's, not the clamp's: overrides.css pins
   `line-height: 1.2 !important` on .page-header__title exactly as the live
   site does, so 24px sits on 28.8px and 36px on 43.2px at every width. The
   value below agrees with it rather than fighting it — v2 previously ran 1.0
   here and 1.25/1.28 below 992px, which made the hero block 2px taller on a
   phone and pushed the case-study subtitle 9px down the disc (owner request,
   2026-11-09). */
@media (min-width: 992px) {
  .page-header__title {
    font-size: clamp(48px, 8.6vw, 150px);
    line-height: 1.2;
  }
}

.page-header__title {
  font-weight: 650;
  letter-spacing: var(--v2-track);
  margin-bottom: 0;
}

/* Case-study heroes keep the same voice at a lower volume.

   Desktop only, for the same reason as the display title: production runs the
   case title on main.css's ladder below 992px — 24px, then 28px from 768px.
   The un-gated clamp forced 36px on a phone and 45.6px at 991px, i.e. 12px and
   17.6px larger than production, and the taller block dragged the subtitle
   down with it. */
@media (min-width: 992px) {
  .page-header__title_case {
    font-size: clamp(36px, 4.6vw, 72px);
    line-height: 1.05;
  }
}

/* Full white at the owner's request — the old 72% wash read as dim next to
   the title. The light theme darkens this for its light hero (overrides.css). */
.page-header__subtitle {
  font-weight: 400;
  letter-spacing: -0.01em;
  color: #ffffff;
}

/* Section headings — tighter, lighter, larger where the layout allows. */
.t-main,
.info-block h2,
.posts h2 {
  letter-spacing: var(--v2-track);
  font-weight: 600;
}

/* "Select work" band label — same copy as the live site's heading
   (h2.text-gray-300 inside `.posts` on https://www.ben-hewett.com/). Values read
   from production: "Hanken Grotesk", "DM Sans", sans-serif / normal / optical
   sizing auto / weight 500 / 56px / 1.1 / -0.03em / #d0d5dd / sentence case.
   Scale follows production's own ladder: `h2,.h2` in main.css is 36 / 52 / 72 /
   120, and `.posts h2` (overrides.css, min-width 992) caps the band at 56px /
   1.1 — so the band renders 36 → 52 → 56, and the 1400 step only widens the
   gap. Hanken Grotesk's italic faces are declared in fonts.css, but production
   renders this heading upright (normal face loaded, every italic face
   unloaded), so there is deliberately no font-style: italic here.
   (Was a 13px uppercase eyebrow: right colour, wrong treatment — the owner
   asked for the live headline treatment, not just the grey.) */
.posts h2.section-eyebrow,
.section-eyebrow {
  display: block;
  font-family: "Hanken Grotesk", "DM Sans", sans-serif;
  font-style: normal;
  font-optical-sizing: auto;
  font-weight: 500;
  font-size: 36px;
  line-height: 1.28;
  letter-spacing: -0.01em;
  text-transform: none;
  color: #d0d5dd;
  margin-bottom: 24px;
}

@media (min-width: 768px) {
  .posts h2.section-eyebrow,
  .section-eyebrow {
    font-size: 52px;
    line-height: 1.19;
    letter-spacing: -0.02em;
    margin-bottom: 32px;
  }
}

@media (min-width: 992px) {
  .posts h2.section-eyebrow,
  .section-eyebrow {
    font-size: 56px;
    line-height: 1.1;
    letter-spacing: -0.03em;
    margin-bottom: 48px;
  }
}

@media (min-width: 1400px) {
  .posts h2.section-eyebrow,
  .section-eyebrow {
    margin-bottom: 72px;
  }
}

.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 8px;
}

/* ==========================================================================
   3. Hero — full viewport, title overlapping the disc, scroll cue
   ========================================================================== */

/* The hero is content-height at every width, exactly as production sizes it:
   the section takes its padding from main.css/overrides.css and the disc sets
   the rest, so the homepage header measures 500px at 375, 685px at 768 and
   638px at 1280+ — production's own numbers.

   The v2 full-viewport hero (min-height: 100vh + flex centring) forced 900px
   at every width and pushed the title block down with it (owner request,
   2026-11-09). `body.home .page-header .container { width: 100% }` went with
   it — it only existed to give the flex child a width. */

/* The disc becomes a backdrop for the type rather than its container. */
.page-header__circle-inner {
  overflow: visible;
  position: relative;
  z-index: 3;
}

.page-header__circle:before {
  opacity: 0.5;
}

/* The icon floats above the title, smaller than before — a mark, not a
   illustration. */
.page-header__icon {
  width: 44px;
  height: 44px;
  margin-bottom: 28px;
}

@media (min-width: 768px) {
  .page-header__icon {
    width: 56px;
    height: 56px;
  }
}

/* Scroll cue — hairline with a travelling segment, bottom centre. */
.page-header__scroll {
  position: absolute;
  left: 50%;
  bottom: 34px;
  transform: translateX(-50%);
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  color: rgba(252, 252, 253, 0.55);
  font-size: 11px;
  font-weight: 550;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  transition:
    opacity 550ms var(--v2-ease),
    transform 550ms var(--v2-ease);
}

/* Dismissed on the first scroll and never restored (motion.js §5b).
   The translateX(-50%) is the element's centring — it has to be repeated
   here or the cue jumps right as it leaves. */
.page-header__scroll.is-dismissed {
  opacity: 0;
  transform: translateX(-50%) translateY(16px);
  pointer-events: none;
}

.page-header__scroll i {
  display: block;
  width: 1px;
  height: 56px;
  background: rgba(252, 252, 253, 0.18);
  position: relative;
  overflow: hidden;
}

.page-header__scroll i:after {
  content: "";
  position: absolute;
  left: 0;
  top: -50%;
  width: 100%;
  height: 50%;
  background: var(--v2-white);
  animation: scroll-cue 2.2s var(--v2-ease) infinite;
}

@keyframes scroll-cue {
  0%   { top: -50%; }
  60%,
  100% { top: 110%; }
}

@media (prefers-reduced-motion: reduce) {
  .page-header__scroll i:after {
    animation: none;
  }

  .work-float {
    display: none;
  }
}

/* ==========================================================================
   5. Work list — production styling, untouched

   The v2 treatment (index numbers, full-width white sweep, arrow reveal,
   text inversion) is deliberately retired: the list matches production —
   hairline dividers, the 18px per-case brand bar sliding in from the
   left, translucent gray row hover, white text throughout. All of that
   lives in main.css and this layer must not fight it.

   The interactions that DO stay are cursor-side, not list-side:
     · the "View" badge (§6d) — the cursor fills over .posts__item-inner
     · the floating preview (§6c) — reads data-preview off the same link
   Neither touches the row's own styling, so the list still reads exactly
   as production does when the pointer is elsewhere.

   The one structural change: the row's vertical padding moved off the
   <article> (zeroed in overrides.css) and onto the <a> below, so the
   link fills the row top to bottom — the entire band is now clickable
   rather than just the text. Values are production's, breakpoint for
   breakpoint. The anchor needs display:block to take them, and the
   article keeps position:relative, so the brand bar and hover
   background still cover the full row height.
   ========================================================================== */

/* Band padding — the "Select work" list sits flush with the section above at
   every width. The 32px desktop opening added 2026-11-09 is gone (owner
   request, 2026-11-10). Only the TOP is touched: main.css's ladder (64 / 87 /
   127 / 144px top and bottom) still sets the closing space, and the base rule
   here also cancels the 87px tablet step so 768-991px keeps the mobile value.
   The ≥992 block is kept — it is the rule that must beat main.css's 127px at
   the breakpoint the request names, and it documents the value in place. */
.posts {
  padding-top: 0;
}

@media (min-width: 992px) {
  .posts {
    padding-top: 0px;
  }
}

.posts__item-inner {
  display: block;
  padding: 24px 0 28px;
}

@media (min-width: 768px) {
  .posts__item-inner {
    padding: 32px 0 28px;
  }
}

@media (min-width: 992px) {
  .posts__item-inner {
    padding: 32px 0 36px;
  }
}

@media (min-width: 1400px) {
  .posts__item-inner {
    padding: 40px 0;
  }
}

/* ==========================================================================
   6. Footer — giant CTA
   ========================================================================== */

/* Static px only — no clamp()/vw, so the CTA is the same size at every
   viewport width inside a breakpoint. Values are production's (main.css):
   30 / 52 / 72px, weight 500, line-height 1.33 / 1.19 / 1.14,
   margin-bottom 22 / 24 / 32px. Tracking comes from overrides.css (-1px). */
.page-footer__title {
  font-size: 30px;
  font-weight: 500;
  line-height: 1.33;
  margin-bottom: 22px;
}

@media (min-width: 768px) {
  .page-footer__title {
    font-size: 52px;
    line-height: 1.19;
    margin-bottom: 24px;
  }
}

@media (min-width: 992px) {
  .page-footer__title {
    font-size: 72px;
    line-height: 1.14;
    margin-bottom: 32px;
  }
}

/* The big footer link takes the live site's hover: the colour shifts
   #98a2b3 -> #364152 (main.css) with no underline. v2 drew an underline
   with a scaleX(0) ::after here; that is retired so the link matches
   live, where the element has no ::after box at all.

   `display: table` rather than block or inline-block: it shrink-wraps to
   the text AND still honours `margin-left: auto`, which parks the link
   against the right edge of its col-lg-7. An inline-block would sit
   stubbornly at the left of the column instead. */
.page-footer__link,
.page-footer__link-wrapper a {
  display: table;
  margin-left: auto;
  text-align: right;
  letter-spacing: var(--v2-track);
  font-weight: 500;
  transition: color 450ms var(--v2-ease);
}

/* Production parks the bare footer CTA 10px left of its column edge on phones
   (overrides.css: `transform: translate(-10px, 0)` under max-width 500px), so
   "Inspiration ›" sits at the column's inner edge rather than hard against it.
   The v2 footer is display:table + margin-left:auto, so the same 10px has to be
   a margin — a transform here would be overwritten by the reveal's own
   translate3d(0, 0, 0) on [data-reveal]. Only .page-footer__link takes it:
   production nudges nothing inside .page-footer__link-wrapper, and that anchor
   already lands on production's edge. */
@media (max-width: 500px) {
  .page-footer__link {
    margin-right: 10px;
  }
}

.page-footer__cop {
  font-size: 12px;
  line-height: 1.5;
  color: var(--v2-gray-dark);
}

@media (min-width: 992px) {
  .page-footer__cop {
    font-size: 14px;
    line-height: 1.43;
  }
}

/* ==========================================================================
   6b. Floating work preview — lerped to the cursor over work rows
   ========================================================================== */

.work-float {
  position: fixed;
  top: -160px;
  left: -220px;
  width: 440px;
  height: 320px;
  z-index: 9000;
  pointer-events: none;
  overflow: hidden;
  /* Collapsed to a horizontal slit; opens like an iris on hover. */
  clip-path: inset(50% 0 50% 0);
  transition: clip-path 520ms var(--v2-ease);
  will-change: transform, clip-path;
}

.work-float img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.14);
  transition: transform 680ms var(--v2-ease);
}

.work-float.is-visible {
  clip-path: inset(0 0 0 0);
}

.work-float.is-visible img {
  transform: scale(1);
}

@media (max-width: 991px) {
  .work-float {
    display: none;
  }
}

/* ==========================================================================
   6d. Cursor "View" badge — the ring becomes a filled disc with a label
   over work rows. Blend is switched off so the disc renders true; the
   palette inverts per theme to keep contrast against the sweep.
   ========================================================================== */

.bh-cursor__label,
.bh-cursor__lock {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--v2-white);
  opacity: 0;
  transition: opacity 250ms ease;
}

.bh-cursor__label {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.bh-cursor.is-media {
  mix-blend-mode: normal;
}

.bh-cursor.is-media .bh-cursor__ring {
  top: -46px;
  left: -46px;
  width: 92px;
  height: 92px;
  background: var(--v2-navy);
  border-color: transparent;
}

.bh-cursor.is-media .bh-cursor__label {
  opacity: 1;
}

/* Locked cases show a padlock instead of the word. */
.bh-cursor.is-media.is-locked .bh-cursor__label {
  opacity: 0;
}

.bh-cursor.is-media.is-locked .bh-cursor__lock {
  opacity: 1;
}

.bh-cursor.is-media .bh-cursor__dot {
  opacity: 0;
}

html.light-theme .bh-cursor.is-media .bh-cursor__ring {
  background: var(--v2-white);
}

html.light-theme .bh-cursor.is-media .bh-cursor__label,
html.light-theme .bh-cursor.is-media .bh-cursor__lock {
  color: var(--v2-navy);
}

/* ==========================================================================
   6e. Footer email — the real CTA, huge
   ========================================================================== */

.page-footer__email {
  display: inline-block;
  position: relative;
  font-size: clamp(24px, 3.4vw, 56px);
  font-weight: 600;
  letter-spacing: var(--v2-track);
  line-height: 1.15;
  color: var(--v2-navy);
  margin-bottom: 44px;
  text-decoration: none;
}

.page-footer__email:after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -6px;
  width: 100%;
  height: 2px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right center;
  transition: transform 600ms var(--v2-ease);
}

.page-footer__email:hover:after {
  transform: scaleX(1);
  transform-origin: left center;
}

/* ==========================================================================
   6f. Password gate — the "Under wraps." page

   The form is the whole page, so it gets the full editorial treatment:
   eyebrow, display headline, a hairline field with an arrow submit, an
   inline error line, and a skip link to the next case. The case title is
   deliberately absent (NDA). The footer artwork stays dimmed behind.
   ========================================================================== */

/* Specificity note: overrides.css loads after this file and carries the
   legacy `.post-password-form` layout rule — the `.protected` prefix
   keeps these declarations ahead of it. The old rule's flex centring and
   text-align stay in force; only the rhythm changes. */
.protected .post-password-form {
  padding: 24px;
  padding-bottom: 12vh; /* optical centre, replacing the old 30vh push */
  min-height: 100vh;
  min-height: 100svh;
}

.pw-form__title {
  font-size: clamp(52px, 8vw, 120px);
  font-weight: 650;
  line-height: 1.0;
  letter-spacing: var(--v2-track);
  color: var(--v2-navy);
  margin: 0 0 22px;
}

.pw-form__text {
  font-size: 18px;
  line-height: 1.55;
  color: var(--v2-gray-dark);
  margin: 0;
}

/* The field: hairline underline that draws itself on focus, arrow submit
   that nudges on hover. */
.pw-form__field {
  position: relative;
  display: flex;
  align-items: center;
  max-width: 460px;
  margin: 54px auto 0;
  border-bottom: 1px solid rgba(17, 25, 39, 0.22);
}

.pw-form__field:after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -1px;
  width: 100%;
  height: 2px;
  background: var(--v2-navy);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 550ms var(--v2-ease);
}

.pw-form__field:focus-within:after {
  transform: scaleX(1);
}

.pw-form__field input {
  flex: 1 1 auto;
  border: 0;
  background: transparent;
  font-family: inherit;
  font-size: 20px;
  line-height: 1.4;
  color: var(--v2-navy);
  padding: 16px 8px;
  text-align: left;
}

.pw-form__field input:focus {
  outline: none;
}

.pw-form__field input::placeholder {
  color: rgba(102, 112, 133, 0.55);
}

.pw-form__submit {
  flex: 0 0 auto;
  border: 0;
  background: transparent;
  color: var(--v2-navy);
  cursor: pointer;
  padding: 12px 4px 12px 16px;
  display: flex;
  align-items: center;
  transition: transform 400ms var(--v2-ease);
}

.pw-form__submit:hover {
  transform: translate3d(7px, 0, 0);
}

/* Error line — hidden until JS adds .is-error after a failed attempt. */
.pw-form__error {
  display: none;
  margin: 18px 0 0;
  font-size: 14px;
  letter-spacing: 0.02em;
  color: #d92d20;
}

.post-password-form.is-error .pw-form__error {
  display: block;
}

.post-password-form.is-error .pw-form__field:after {
  background: #d92d20;
  transform: scaleX(1);
}

/* The skip link — quiet by default, arrow slides on hover. */
.pw-form__skip {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  margin-top: 64px;
  font-size: 15px;
  font-weight: 550;
  letter-spacing: 0.06em;
  color: var(--v2-gray-dark);
  text-decoration: none;
  transition: color 400ms var(--v2-ease);
}

.pw-form__skip svg {
  transition: transform 450ms var(--v2-ease);
}

.pw-form__skip:hover {
  color: var(--v2-navy);
}

.pw-form__skip:hover svg {
  transform: translate3d(6px, 0, 0);
}

@media (max-width: 600px) {
  .pw-form__field {
    max-width: 100%;
  }
}

/* ==========================================================================
   6g. Menu buttons — open and close

   Both are empty buttons with an SVG background, and both are themed
   with filter: invert() elsewhere, so the hover has to stay on transform
   and opacity — touching `filter` here would wipe the inversion and flip
   the icon to the wrong colour mid-hover.

   translateZ(0) is repeated in every transform because .reset-button
   sets it for font smoothing; a bare scale/rotate would drop it.
   ========================================================================== */

.page-nav__burger {
  transform: translateZ(0) scaleX(1);
  transform-origin: right center;
  transition:
    transform 450ms var(--v2-ease),
    opacity 350ms var(--v2-ease);
}

/* The bars stretch out from the edge they are already anchored to —
   background-position is right center, so the growth reads as the menu
   widening rather than the icon sliding. */
.page-nav__burger:hover {
  transform: translateZ(0) scaleX(1.12);
  opacity: 0.72;
}

.sidebar__close {
  transform: translateZ(0) scale(1);
  transition:
    transform 420ms var(--v2-ease),
    opacity 350ms var(--v2-ease);
}

/* Contracts slightly — the icon pulls in towards itself, which reads as
   a press and suits a dismiss action better than a spin. */
.sidebar__close:hover {
  transform: translateZ(0) scale(0.88);
  opacity: 0.75;
}

@media (prefers-reduced-motion: reduce) {
  .page-nav__burger:hover,
  .sidebar__close:hover {
    transform: translateZ(0);
  }
}

/* ==========================================================================
   7. Details
   ========================================================================== */

/* Film grain — static turbulence tile at 5% over everything but the
   curtain. Static, not animated: animated grain costs a repaint every
   frame on the whole viewport. */
body:after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 9998;
  pointer-events: none;
  opacity: 0.05;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

::selection {
  background: var(--v2-gray);
  color: var(--v2-ink);
}

:focus-visible {
  outline: 2px solid var(--v2-gray);
  outline-offset: 3px;
  border-radius: 2px;
}

/* Work-list dividers and light-theme sweep overrides removed with the v2
   list treatment (see §5) — the per-case brand bar must keep its inline
   colour in both themes, and the dividers stay at the production value. */
