/* ==========================================================================
   THROO PORTFOLIO — styles.css
   Fixed order: tokens, reset, typography, layout primitives,
   page sections in page order, module styles, utilities, media queries.
   No literal color / size / spacing / font value outside :root. See
   EXTENDING.md for how to change the design system in one edit.
   ========================================================================== */

/* ==========================================================================
   TOKENS
   ========================================================================== */

@font-face {
  font-family: "Clash Display";
  src: url("assets/fonts/ClashDisplay-Bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "General Sans";
  src: url("assets/fonts/GeneralSans-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "General Sans";
  src: url("assets/fonts/GeneralSans-Medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "General Sans";
  src: url("assets/fonts/GeneralSans-Semibold.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("assets/fonts/JetBrainsMono-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("assets/fonts/JetBrainsMono-Bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
/* Poppins: phone-creative only. Not used until the cp- module lands. */
@font-face {
  font-family: "Poppins";
  src: url("assets/fonts/poppins-v24-latin-regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

:root {
  /* color — page palette, from DESIGN.md §11 */
  --bg:            #FAFAFA;
  --bg-alt:        #E1D5C2;
  --ink:           #262626;
  --ink-muted:     #605F5F;
  --rule:          #E4E8EA;
  --accent:        #EE4434;

  --bg-throo:      #FFF9E5;
  --bg-close:      #EE4434;
  --ink-inverse:   #FAFAFA;
  --card-ground:   #EDEDED;

  /* color — added, following the existing naming pattern (not frozen names) */
  --ink-hover:     #3D3D3D;
  --rule-dashed:   #C9CFD1;

  /* Sept 14 changeset 2, item 9: restoring ground rhythm after Why Throo's
     removal took the warm yellow with it. Dean's cream, extracted in
     DESIGN.md §1/§3 as a standing collision but never assigned a token —
     used here instead of reusing --bg-throo, which stays Throo-scoped only. */
  --bg-warm:       #F3E8D6;

  /* Sept 14 changeset 2, item 8: per-venture brand accents, sampled directly
     from each venture's own creative in assets/ (see EXTENDING.md for the
     source pixels). Both are darkened at constant hue until they clear
     4.5:1 against every ground they render on as text: AllFactor's sampled
     #2562E9 was 4.33:1 on --bg-warm, now #1654DE (5.16:1); HIII's sampled
     #C0D7A3 was 1.49:1, now #455C28 (5.15:1 on --bg-alt). The true sampled
     HIII tone survives as --brand-hiii-light for non-text accents. */
  --brand-allfactor:   #1654DE;
  --brand-hiii:        #455C28;
  --brand-hiii-light:  #C0D7A3;

  /* Sept 14 changeset 2, item 13: shadow, approved for the top bar's pills
     only. Nothing else on the page uses one. */
  --shadow-pill: 0 2px 6px rgba(38, 38, 38, 0.15);

  /* changeset 4: light orange for the identity card's contact strip,
     derived from --accent's hue shifted warmer (--accent itself reads more
     red than orange) and lightened well past any of the page's other warm
     grounds. See EXTENDING.md for why this isn't Askumi's banned marquee. */
  --card-strip: #F4DFCC;

  /* changeset 5: hero ribbon decoration, quiet pastel tones in the same
     warm family as --card-strip/--bg-warm rather than louder/more saturated
     picks, since these are meant to read as background texture behind the
     card, not compete with it. */
  --ribbon-yellow: #F1E8CF;
  --ribbon-orange: #F0D9C5;

  /* type */
  --font-display:  "Clash Display", system-ui, sans-serif;
  --font-body:     "General Sans", system-ui, sans-serif;
  --font-label:    "JetBrains Mono", ui-monospace, monospace;
  --text-xs:       0.8125rem;
  --text-sm:       0.9375rem;
  --text-base:     clamp(1.0625rem, 1rem + 0.3vw, 1.1875rem);
  --text-lg:       clamp(1.25rem, 1.15rem + 0.45vw, 1.5rem);
  --text-xl:       clamp(1.75rem, 1.3rem + 2vw, 2.625rem);
  --text-2xl:      clamp(2.625rem, 1.6rem + 4.6vw, 4.375rem);
  --leading-tight: 1.1;
  --leading-body:  1.5;
  --measure:       66ch;

  /* space */
  --space-unit:    4px;
  --space-block:   24px;
  --space-section: clamp(96px, 8vw, 128px);
  --page-max:      1100px;
  --page-pad:      clamp(20px, 4vw, 40px);

  /* added, following the existing naming pattern */
  --radius-card:   16px;
  --radius-image:  8px;
  --radius-pill:   9999px;
  --border-hair:   1px;
  --border-thin:   1.5px;
  --border-thick:  2px;
  --duration-fast:  120ms;
  --duration-state: 220ms;
  --focus-offset:  3px;
  --size-avatar:   48px;
  --topbar-pad-y:  calc(var(--space-unit, 4px) * 6);

  /* Phase 1 close-out audit: values that were previously literal outside
     :root (BUILD_BRIEF §10), now tokens so a reskin stays a :root edit. */
  --if-panel-ground: #F2F6F7;   /* DESIGN.md §9 folder-tab panel */
  --card-max:        640px;
  --card-photo-min:  120px;
  --card-name-size:  1.25rem;   /* DESIGN.md §6, 20px */
  --text-2xs:        0.75rem;   /* card strip below 400px only */
  --icon-size:       14px;
  --barcode-max:     220px;
  --barcode-height:  32px;
  --gallery-col-min: 220px;

  /* if- folder skin (design 6a), DESIGN.md §9. The straw palette, the folder's
     depth shadows and its three radii. --if-panel-ground above still dresses
     hiii.html's tabs, which keep the flat Phase 1 treatment. */
  --if-straw:          #F7E6A0;   /* jacket, and the pulled tab */
  --if-straw-seated:   #EAD68A;   /* tabs sitting down in the folder */
  --if-straw-back:     #DEC877;   /* back cover peeking out behind */
  --if-straw-pocket:   #F0DC8C;   /* slanted front pocket */
  --if-sheet:          #FFFFFF;
  --if-sheet-rule:     #EFE9D8;   /* the sheet's ruling, and image borders */
  --if-sheet-rule-gap: 28px;
  --if-tab-reinforce:  rgba(120, 95, 30, 0.16);
  --if-brass-back:     #A8893A;   /* gem clip, the loop behind the sheet */
  --if-brass-front:    #C9A94F;   /* gem clip, the loop over the sheet */
  --if-clip-steel:     #9A9A9A;   /* binder clip wire handle */
  --if-clip-highlight: #4A4A4A;   /* binder clip body highlight */
  --if-back-offset:    8px;       /* how far the back cover clears the jacket */
  --if-pocket-height:  48px;
  --radius-if-tab:     12px;
  --radius-if-folder:  14px;
  --radius-if-sheet:   3px;
  --shadow-if-folder:     0 14px 26px -14px rgba(38, 38, 38, 0.4);
  --shadow-if-sheet:      inset 0 5px 10px -6px rgba(38, 38, 38, 0.45);
  --shadow-if-tab-seated: inset 0 -7px 9px -6px rgba(38, 38, 38, 0.28);
  --shadow-if-tab-pulled: 0 -6px 14px -4px rgba(38, 38, 38, 0.2);
  --shadow-if-pocket:     drop-shadow(0 -3px 3px rgba(38, 38, 38, 0.3));
  /* if- folder motion. The spring overshoots slightly so the tab reads as
     pulled up out of the folder rather than sliding. */
  --duration-if-tab:    340ms;
  --duration-if-fade:   260ms;
  --duration-if-reveal: 300ms;
  --ease-if-spring:     cubic-bezier(0.34, 1.5, 0.5, 1);
  --ease-if-reveal:     cubic-bezier(0.2, 0.8, 0.3, 1);
  --if-reveal-rise:     10px;

  /* state */
  --focus:         2px solid #262626;
}

/* Throo values: scoped, never global. Used only inside .why-throo and by
   the sm- / cp- module prefixes. */
.why-throo {
  --throo-ground:  #FFF9E5;
  --throo-ink:     #0A2E3D;
  --throo-ink-70:  #2C525F;
  --throo-yellow:  #FCC601;
  --throo-signal:  #FFC400;
  --throo-green:   #30C353;
  --throo-paper:   #FFFFFF;
  --throo-paper-3: #E8EEEF;
}

/* ==========================================================================
   RESET
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--bg, #FAFAFA);
  color: var(--ink, #262626);
}
img { display: block; max-width: 100%; }
a { color: inherit; }
button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
ul, ol { margin: 0; padding: 0; list-style: none; }
h1, h2, h3, p { margin: 0; }

/* ==========================================================================
   TYPOGRAPHY
   ========================================================================== */

body {
  font-family: var(--font-body, system-ui, sans-serif);
  font-weight: 400;
  font-size: var(--text-base, 1.0625rem);
  line-height: var(--leading-body, 1.5);
}

.headline,
.section-heading {
  font-family: var(--font-display, system-ui, sans-serif);
  font-weight: 700;
  line-height: var(--leading-tight, 1.1);
  letter-spacing: 0;
  color: var(--ink, #262626);
}
.headline   { font-size: var(--text-2xl, 2.625rem); }
.section-heading { font-size: var(--text-xl, 1.75rem); }
.bg-close .section-heading { color: var(--ink-inverse, #FAFAFA); }

.intro,
.hook {
  font-family: var(--font-body, system-ui, sans-serif);
  font-weight: 600;
  font-size: var(--text-lg, 1.25rem);
  line-height: var(--leading-body, 1.5);
  max-width: var(--measure, 66ch);
}

.prose {
  font-family: var(--font-body, system-ui, sans-serif);
  font-weight: 400;
  font-size: var(--text-base, 1.0625rem);
  line-height: var(--leading-body, 1.5);
  max-width: var(--measure, 66ch);
}
.prose + .prose { margin-top: var(--space-block, 24px); }

.caption {
  font-family: var(--font-body, system-ui, sans-serif);
  font-size: var(--text-sm, 0.9375rem);
  color: var(--ink-muted, #605F5F);
  max-width: var(--measure, 66ch);
  margin-top: calc(var(--space-unit, 4px) * 2);
}

.label,
.bracket {
  font-family: var(--font-label, monospace);
  font-size: var(--text-xs, 0.8125rem);
  line-height: 1.4;
  color: var(--ink-muted, #605F5F);
}
.bracket::before { content: "[ "; }
.bracket::after  { content: " ]"; }

/* ==========================================================================
   LAYOUT PRIMITIVES
   ========================================================================== */

.ground {
  width: 100%;
  padding-block: var(--space-section, 96px);
}
.column {
  max-width: var(--page-max, 1100px);
  margin-inline: auto;
  padding-inline: var(--page-pad, 20px);
}
.stack > * + * { margin-top: var(--space-block, 24px); }

.skip-link {
  position: absolute;
  left: var(--page-pad, 20px);
  top: -100px;
  background: var(--ink, #262626);
  color: var(--ink-inverse, #FAFAFA);
  padding: calc(var(--space-unit, 4px) * 3) calc(var(--space-unit, 4px) * 5);
  border-radius: var(--radius-pill, 9999px);
  z-index: 10;
  transition: top var(--duration-fast, 120ms) ease-out;
}
.skip-link:focus { top: var(--page-pad, 20px); }

.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--gallery-col-min, 220px), 1fr));
  gap: var(--space-block, 24px);
}
.gallery figure { margin: 0; }
.gallery img {
  width: 100%;
  height: auto;
  border-radius: var(--radius-image, 8px);
}

/* focus ring, everywhere */
a:focus-visible,
button:focus-visible,
[tabindex]:focus-visible {
  outline: var(--focus, 2px solid #262626);
  outline-offset: var(--focus-offset, 3px);
}
.bg-close a:focus-visible,
.bg-close button:focus-visible {
  outline: var(--border-thick, 2px) solid var(--ink-inverse, #FAFAFA);
}

/* button repertoire */
.btn {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--space-unit, 4px) * 2);
  font-family: var(--font-label, monospace);
  text-decoration: none;
  border-radius: var(--radius-pill, 9999px);
}
.btn-solid {
  background: var(--ink, #262626);
  color: var(--ink-inverse, #FAFAFA);
  font-size: var(--text-sm, 0.9375rem);
  padding: calc(var(--space-unit, 4px) * 3.5) calc(var(--space-unit, 4px) * 7);
  transition: background var(--duration-fast, 120ms) ease-out;
}
.btn-solid:hover { background: var(--ink-hover, #3D3D3D); }
.bg-close .btn-solid { background: var(--ink, #262626); color: var(--ink-inverse, #FAFAFA); }

.btn-outline {
  background: transparent;
  border: var(--border-thin, 1.5px) solid var(--ink, #262626);
  color: var(--ink, #262626);
  font-size: var(--text-xs, 0.8125rem);
  padding: calc(var(--space-unit, 4px) * 2) calc(var(--space-unit, 4px) * 4.5);
  transition: border-color var(--duration-fast, 120ms) ease-out, color var(--duration-fast, 120ms) ease-out;
}
.btn-outline:hover { border-color: var(--accent, #EE4434); color: var(--accent, #EE4434); }

.link-underline {
  color: var(--ink, #262626);
  text-decoration: underline;
  text-underline-offset: var(--focus-offset, 3px);
  text-decoration-thickness: var(--border-hair, 1px);
  transition: color var(--duration-fast, 120ms) ease-out, text-decoration-thickness var(--duration-fast, 120ms) ease-out;
}
.link-underline:hover {
  color: var(--accent, #EE4434);
  text-decoration-thickness: var(--border-thick, 2px);
}

/* ==========================================================================
   PAGE SECTIONS, in page order
   ========================================================================== */

/* ---- top bar. Same component on all three pages; build once. ---- */
.topbar { background: var(--bg, #FAFAFA); position: static; }
/* changeset 3, item 5: a tight, centered cluster (deanira.co) rather than
   the three elements pinned to the far edges of the page column. Order in
   the markup (Work, avatar, Resume) still reads left-to-right; they're just
   grouped together in the middle instead of spread edge to edge. */
.topbar-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: calc(var(--space-unit, 4px) * 12);
  padding-block: var(--topbar-pad-y, 24px);
}
/* Sept 14 changeset 2, item 14: sized up from the general-purpose
   .btn-outline to match deanira.co's pill proportions. Scoped to the top
   bar only so .btn-outline itself stays the plain treatment-B size
   documented in DESIGN.md for future tags/filters. */
.topbar .btn-outline {
  padding: calc(var(--space-unit, 4px) * 3) calc(var(--space-unit, 4px) * 6);
  font-size: var(--text-sm, 0.9375rem);
  font-weight: 700;
  box-shadow: var(--shadow-pill);
}
.topbar-avatar {
  display: block;
  width: var(--size-avatar, 48px);
  height: var(--size-avatar, 48px);
  border-radius: 50%;
  overflow: hidden;
}
.topbar-avatar img { width: 100%; height: 100%; object-fit: cover; }

@media (max-width: 700px) {
  .topbar-row {
    gap: calc(var(--space-unit, 4px) * 4);
    flex-wrap: wrap;
    row-gap: calc(var(--space-unit, 4px) * 3);
  }
  .topbar .btn-outline {
    padding: calc(var(--space-unit, 4px) * 2) calc(var(--space-unit, 4px) * 4);
  }
}

/* ---- identity ---- */
.identity { background: var(--bg, #FAFAFA); position: relative; overflow: hidden; }

/* ==========================================================================
   HERO RIBBONS — changeset 5. Decorative only. See EXTENDING.md "Hero
   ribbons" for the exact deletion lines to remove motion only, or the
   whole thing.
   ========================================================================== */
.hero-ribbons {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
.hero-ribbons svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.hero-ribbons path {
  fill: none;
  stroke-width: 64;
  stroke-linecap: round;
}
.hero-ribbon-left path { stroke: var(--ribbon-orange, #F0D9C5); }
.hero-ribbon-right path { stroke: var(--ribbon-yellow, #F1E8CF); }

/* ---- Pass B: load-in trace, CSS only. Delete this sub-block (down to
   "END PASS B") to fall back to Pass A's static ribbons; nothing outside
   it references the animation. Wrapped entirely in a no-preference query,
   so prefers-reduced-motion: reduce never sees a dasharray/dashoffset at
   all and paints the finished path on the first frame, same as Pass A. ---- */
@media (prefers-reduced-motion: no-preference) {
  .hero-ribbons path {
    stroke-dasharray: 2200;
    stroke-dashoffset: 2200;
    animation-name: hero-ribbon-draw;
    animation-duration: 850ms;
    animation-timing-function: ease-out;
    animation-fill-mode: forwards;
  }
  @keyframes hero-ribbon-draw {
    to { stroke-dashoffset: 0; }
  }
}
/* ---- END PASS B ---- */

/* Simplify to nothing at narrow widths per the change set: background
   texture is the first thing to go before it risks crowding the card. */
@media (max-width: 480px) {
  .hero-ribbons { display: none; }
}
/* ===== END HERO RIBBONS ===== */

/* Sept 14 changeset 2, item 4: the card reads as a physical card emerging
   from a folder/sleeve behind it. The sleeve is a plain solid-fill rect,
   offset behind and below-right — depth from an edge, not a blur, since
   shadows are off everywhere but the top bar (item 13). */
.identity-card-wrap {
  position: relative;
  z-index: 1;
  max-width: var(--card-max, 640px);
  margin-inline: auto;
}
.identity-card-sleeve {
  position: absolute;
  top: calc(var(--space-unit, 4px) * 3);
  left: calc(var(--space-unit, 4px) * 2);
  right: calc(var(--space-unit, 4px) * -2);
  bottom: calc(var(--space-unit, 4px) * -3);
  background: var(--rule, #E4E8EA);
  border-radius: var(--radius-card, 16px);
  z-index: 0;
}
.identity-card {
  position: relative;
  z-index: 1;
}
/* changeset 4: contact strip along the card's top edge, static (no
   scrolling — see EXTENDING.md for why this isn't Askumi's banned marquee).
   Owns the top corners now; .identity-card-top is square on top since it
   sits directly beneath the strip. */
.identity-card-strip {
  background: var(--card-strip, #F4DFCC);
  border-radius: var(--radius-card, 16px) var(--radius-card, 16px) 0 0;
  padding: calc(var(--space-unit, 4px) * 3) calc(var(--space-unit, 4px) * 6);
}
.identity-card-strip .identity-contact {
  justify-content: center;
  flex-wrap: nowrap;
  gap: calc(var(--space-unit, 4px) * 4) calc(var(--space-unit, 4px) * 5);
}
.identity-card-strip .identity-contact li {
  font-family: var(--font-label, monospace);
  font-size: var(--text-xs, 0.8125rem);
  white-space: nowrap;
}
/* changeset 4: kept to one line at every width per direct request. The
   full email address and LinkedIn URL never fit a 320px-wide strip at any
   readable font size (66+ characters of monospace), so this instance
   (only) uses short generic labels instead of the raw address/URL &mdash;
   "Email" for the visible link text, not the underlying href, which is
   still the real mailto: address. The closing panel's copy of this list
   keeps the full text; it has the width for it and isn't wedged into a
   business-card strip. */
.identity-contact--strip a { white-space: nowrap; }
@media (max-width: 400px) {
  .identity-card-strip .identity-contact { gap: calc(var(--space-unit, 4px) * 3) calc(var(--space-unit, 4px) * 3); }
  .identity-card-strip .identity-contact li { font-size: var(--text-2xs, 0.75rem); }
  .identity-card-strip { padding-inline: calc(var(--space-unit, 4px) * 4); }
}

.identity-card-top {
  background: var(--card-ground, #EDEDED);
  overflow: hidden;
  display: grid;
  grid-template-columns: minmax(var(--card-photo-min, 120px), 1fr) 2fr;
}
/* item 4: closer to real business-card proportions once the layout has
   room to be wide rather than stacked. */
@media (min-width: 641px) {
  .identity-card-top { aspect-ratio: 1.85 / 1; }
}
.identity-card-photo {
  background: var(--ink, #262626);
}
.identity-card-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* item 5: the crop already pushes the subject down; bias the visible
     window toward him rather than the default centered crop. */
  object-position: 42% 32%;
}
.identity-card-text {
  padding: calc(var(--space-unit, 4px) * 6);
  font-family: var(--font-label, monospace);
}
.identity-card-name {
  font-size: var(--card-name-size, 1.25rem);
  font-weight: 700;
  color: var(--accent, #EE4434);
  margin: 0;
}
.identity-card-role {
  font-size: var(--text-sm, 0.9375rem);
  font-weight: 400;
  color: var(--ink, #262626);
  margin-top: var(--space-unit, 4px);
}
.identity-card-rule {
  border: none;
  border-top: var(--border-hair, 1px) solid var(--rule, #E4E8EA);
  margin: calc(var(--space-unit, 4px) * 4) 0;
}
.identity-card-affiliation {
  font-size: var(--text-sm, 0.9375rem);
  color: var(--ink, #262626);
}
.identity-card-affiliation .bracket {
  display: block;
  margin-top: calc(var(--space-unit, 4px) * 1.5);
}
.identity-card-barcode { margin-top: calc(var(--space-unit, 4px) * 5); }
.identity-card-barcode svg { display: block; width: 100%; max-width: var(--barcode-max, 220px); height: var(--barcode-height, 32px); fill: var(--ink, #262626); }

/* Card hover/focus panel. Collapsed is the static default and the card
   above already carries all identity information on its own; the panel
   adds, it does not complete. Height comes from a grid-rows 0fr -> 1fr
   trick so it animates without JS and without a fixed pixel guess. */
.identity-card-panel-track {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--duration-state, 220ms) ease-out;
}
.identity-card-panel-inner {
  overflow: hidden;
  min-height: 0;
  background: var(--accent, #EE4434);
  border-radius: 0 0 var(--radius-card, 16px) var(--radius-card, 16px);
  color: var(--ink-inverse, #FAFAFA);
  text-align: center;
  opacity: 0;
  transition: opacity var(--duration-state, 220ms) ease-out;
  padding: calc(var(--space-unit, 4px) * 6);
}
.identity-card-panel-text {
  font-family: var(--font-label, monospace);
  font-size: var(--text-sm, 0.9375rem);
  max-width: none;
  margin: 0 auto;
}
.identity-card-panel-inner .btn-solid {
  margin-top: calc(var(--space-unit, 4px) * 5);
}

.identity-card:hover .identity-card-panel-track,
.identity-card:focus-within .identity-card-panel-track {
  grid-template-rows: 1fr;
}
.identity-card:hover .identity-card-panel-inner,
.identity-card:focus-within .identity-card-panel-inner {
  opacity: 1;
}

/* changeset 3, item 1: the panel is always in normal document flow (no
   overlay/absolute positioning). Collapsed, its row is 0fr = 0px, so it
   costs nothing at rest on any device. Expanding it grows the real layout
   height and pushes whatever comes after (the AllFactor section) down for
   as long as it's open, instead of overlaying on top of it — the previous
   overlay approach either covered the section below or needed a large
   permanently-reserved gap to avoid it; this needs neither. */

/* Touch, or anything without hover: always expanded, in normal flow, so it
   reserves its own space instead of hiding content behind a hover that
   can't happen. No tap toggle. */
@media (hover: none) {
  .identity-card-panel-track { grid-template-rows: 1fr; }
  .identity-card-panel-inner { opacity: 1; }
}

.identity-contact {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--space-unit, 4px) * 5);
  padding: 0;
}
.identity-contact li { font-size: var(--text-sm, 0.9375rem); }
/* changeset 4: small monochrome line icons next to each contact method,
   inside the link itself so icon and text share one hover color/target.
   Not emoji: CLAUDE.md's own tells list calls out "emoji as icons" as a
   template-y default, and colored glyphs (a yellow phone, a blue LinkedIn
   square) would also break the page's one-accent-color discipline. These
   inherit currentColor instead, so they're ink at rest and match whatever
   the link turns on hover. */
.identity-contact a { display: inline-flex; align-items: center; gap: calc(var(--space-unit, 4px) * 1.5); }
.identity-contact .icon {
  width: var(--icon-size, 14px);
  height: var(--icon-size, 14px);
  flex-shrink: 0;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* changeset 4: contact line moved onto the card's own top strip (see
   .identity-card-strip under "PAGE SECTIONS → identity"), a static
   restyle of the same element askumi.co.in scrolls — sized down and
   centered to fit there, and allowed to wrap onto a second line at narrow
   widths rather than force one line the way changeset 3 item 2 needed to
   when this sat in the full-width column above the card. */

/* ---- why-throo (job-specific): removed from the page, Sept 14 changeset.
   Styles kept here, commented, so reinstating the block is a paste. See
   EXTENDING.md "Bringing back Why Throo" for the full restore procedure. ---- */
/*
.why-throo { background: var(--bg-throo, #FFF9E5); }
.why-throo .section-heading { margin-bottom: var(--space-block, 24px); }
.framing-note {
  font-family: var(--font-body, system-ui, sans-serif);
  font-weight: 500;
  font-size: var(--text-base, 1.0625rem);
  color: var(--ink-muted, #605F5F);
  max-width: none;
  margin-bottom: var(--space-block, 24px);
}
.why-throo .hook { color: var(--throo-ink, #0A2E3D); margin-bottom: var(--space-section, 96px); }
.why-throo-slots {
  display: grid;
  gap: var(--space-block, 24px);
}
*/
/* Empty slots collapse to nothing: no border, no min-height, no "coming soon". */
.slot:empty { display: none; }

/* ---- case studies (shared shape: AllFactor, HIII) ---- */
.case-study .section-heading { margin-bottom: var(--space-block, 24px); }
/* changeset 4: prose-to-gallery gap trimmed by roughly two lines of body
   text (~56px at --text-base/1.5 line-height) per direct request. Applies
   to AllFactor's own gallery and the CEO sub-gallery, neither of which has
   a button in between to carry part of the gap. */
.case-study .gallery { margin-top: calc(var(--space-block, 24px) * 3); }

/* Sept 14 changeset 2, item 8 (OVERRIDE): the venture name in that
   venture's own sampled brand color, the rest of the heading in --ink. The
   identity card stays the one loud object elsewhere on the page; this is
   the one approved exception, scoped to two headings only. */
.brand-name { }
.brand-name--allfactor { color: var(--brand-allfactor, #1654DE); }
.brand-name--hiii { color: var(--brand-hiii, #455C28); }

/* A heading that is also a link to the subpage (item 12): inherits the
   heading's own color/weight, no underline, brand-colored hover/focus. */
.heading-link { color: inherit; text-decoration: none; }
.hiii .heading-link:hover,
.hiii .heading-link:focus-visible { color: var(--brand-hiii, #455C28); }
.interfaces-teaser .heading-link:hover,
.interfaces-teaser .heading-link:focus-visible { color: var(--accent, #EE4434); }

/* A small brand-colored rule under a case-study heading: one of the "small
   amounts... bubbles, rules, link hovers" item 8 allows into each venture's
   own panel. Not a ground, not a fill. */
.brand-rule {
  width: calc(var(--space-unit, 4px) * 10);
  height: var(--border-thick, 2px);
  margin-top: calc(var(--space-unit, 4px) * 3);
  margin-bottom: var(--space-block, 24px);
}
.brand-rule--allfactor { background: var(--brand-allfactor, #1654DE); }
.brand-rule--hiii { background: var(--brand-hiii, #455C28); }

/* Tag chip: rolled out to every gallery on all three pages (Sept 14
   changeset 2, item 10). Bubbles replace prose captions, they don't sit
   alongside them. Static label, not a button/link: no hover state. */
.tag-chip {
  display: inline-block;
  font-family: var(--font-label, monospace);
  font-size: var(--text-xs, 0.8125rem);
  color: var(--ink, #262626);
  /* changeset 3, item 7: --rule (#E4E8EA, a cool near-white hairline color)
     was never meant to carry contrast on its own — it reads fine as a card
     divider but nearly vanishes as a chip border on any warm/tinted ground
     (the CEO club's beige, HIII's tan). --ink everywhere a section doesn't
     supply its own brand color instead. */
  border: var(--border-thin, 1.5px) solid var(--ink, #262626);
  border-radius: var(--radius-pill, 9999px);
  padding: calc(var(--space-unit, 4px) * 1) calc(var(--space-unit, 4px) * 3);
  margin-top: calc(var(--space-unit, 4px) * 2);
  margin-right: calc(var(--space-unit, 4px) * 1);
}
/* item 8: small brand-colored amounts carried into each venture's own
   chips. Scoped past the CEO sub-block (a nested .stack), which stays
   neutral since the CEO creative isn't the AllFactor or HIII brand. */
.allfactor > .column > .gallery .tag-chip {
  border-color: var(--brand-allfactor, #1654DE);
  color: var(--brand-allfactor, #1654DE);
}
.hiii .tag-chip {
  border-color: var(--brand-hiii, #455C28);
  color: var(--brand-hiii, #455C28);
}

.allfactor { background: var(--bg-warm, #F3E8D6); }
/* Scoped to .case-study.hiii (the landing-page section) rather than plain
   .hiii, so hiii.html can carry the .hiii class for its brand-colored chips
   (below) without also picking up this tan ground — DESIGN.md keeps
   subpages on --bg throughout, landing page is where the color rhythm lives. */
.case-study.hiii { background: var(--bg-alt, #E1D5C2); }

/* Sept 14 changeset 2/3, items 6-7 (then 3-4): deliberate, filled
   arrangements instead of the default sparse auto-fit wrap. Changeset 2's
   version of the AllFactor grid left row 2's other two cells empty (the
   "large empty gap" changeset 3 item 3 flagged); fixed by having the two
   email images each span both rows, so every cell is claimed.

   Changeset 4, item 2: that fix cropped every image to fill its cell
   (height: 100% + object-fit: cover), which is what actually caused the
   problem being fixed here — the Google Ads screenshot losing its left
   edge and labels, Mountix slides cut mid-word, chips overflowing a
   height-constrained figure and landing underneath the next row's image.
   Removed entirely. No fixed height, no object-fit, no forced
   aspect-ratio anywhere in this block: every image renders at its own
   natural aspect ratio, scaled by width only (the base ".gallery img"
   rule, width: 100%; height: auto, already does exactly this — the
   override below was fighting it). Rows are `auto`, not `minmax(..., 1fr)`,
   so a cell is exactly as tall as its own image needs and neighboring
   cells don't stretch, crop, or bottom-align to match it. Uneven column
   heights are correct, not a bug. */
.gallery-arranged {
  display: grid;
  gap: var(--space-block, 24px);
  align-items: start;
}

.allfactor-gallery {
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: auto auto;
  grid-template-areas:
    "openrate email1 email2"
    "ads      email1 email2";
}
.allfactor-gallery .gallery-openrate { grid-area: openrate; }
.allfactor-gallery .gallery-email1   { grid-area: email1; }
.allfactor-gallery .gallery-email2   { grid-area: email2; }
.allfactor-gallery .gallery-ads      { grid-area: ads; }

/* item 4: golf photo and the product post each span the full row-count in
   their own column; the three slide-deck images share the right column,
   one per row. Positions unchanged from changeset 3 — only the cropping
   (above) changed in changeset 4. */
.hiii-gallery {
  grid-template-columns: 1fr 1.3fr 1fr;
  grid-template-rows: auto auto auto;
  grid-template-areas:
    "golf product slide1"
    "golf product slide2"
    "golf product slide3";
}
.hiii-gallery .gallery-golf    { grid-area: golf; }
.hiii-gallery .gallery-product { grid-area: product; }
.hiii-gallery .gallery-slide1  { grid-area: slide1; }
.hiii-gallery .gallery-slide2  { grid-area: slide2; }
.hiii-gallery .gallery-slide3  { grid-area: slide3; }

@media (max-width: 640px) {
  .allfactor-gallery {
    grid-template-columns: 1fr;
    grid-template-rows: none;
    grid-template-areas: "openrate" "email1" "email2" "ads";
  }
  .hiii-gallery {
    grid-template-columns: 1fr;
    grid-template-rows: none;
    grid-template-areas: "product" "golf" "slide1" "slide2" "slide3";
  }
}

/* ---- interfaces teaser ---- */
.interfaces-teaser { background: var(--bg, #FAFAFA); }

/* changeset 3, item 6: the callout button was flush against the prose
   above it (no rule set a gap) and then the gallery followed with a full
   --space-section void, so the rhythm read as broken rather than building.
   Both gaps now come from --space-block multiples that relate to each
   other: 1x above the button (following the prose/label directly above
   it), 3x below (also set on the following .gallery, above) before the
   gallery starts. */
.hiii .btn-solid,
.interfaces-teaser .btn-solid {
  margin-top: var(--space-block, 24px);
  margin-bottom: calc(var(--space-block, 24px) * 3);
}
/* changeset 4: the button's own margin-bottom (above) and the gallery's own
   margin-top don't collapse into each other (the button is inline-flex, not
   block), so on interfaces they were stacking into a much bigger gap than
   on HIII, where the gallery's margin-top was already zeroed. Trimmed here
   instead of touching the shared button rule above, so the prose-to-button
   gap stays identical on both sections. */
.hiii .hiii-gallery { margin-top: 0; }
.hiii .btn-solid { margin-bottom: calc(var(--space-block, 24px) * 2); }
.interfaces-teaser .gallery { margin-top: calc(var(--space-unit, 4px) * 4); }

/* ---- closing ---- */
.closing {
  background: var(--bg-close, #EE4434);
  color: var(--ink-inverse, #FAFAFA);
}
.closing .prose { color: var(--ink-inverse, #FAFAFA); }
.closing .identity-contact a { color: var(--ink-inverse, #FAFAFA); }
.closing .btn-solid { margin-top: var(--space-block, 24px); }

/* ---- subpage: back link, project list ---- */
.subpage-nav { background: var(--bg, #FAFAFA); }
.subpage-nav .column { padding-block: calc(var(--space-unit, 4px) * 6); }
/* item 17: same pill treatment as the top bar, not plain text. */
.subpage-nav .btn-outline { display: inline-flex; }
.project-list { display: grid; gap: var(--space-section, 96px); margin-top: var(--space-section, 96px); }
.project { display: grid; gap: var(--space-block, 24px); }
.project img { border-radius: var(--radius-image, 8px); }
.project-meta { display: flex; flex-wrap: wrap; gap: calc(var(--space-unit, 4px) * 5); }

/* item 16: the subpage intro and the tab row were touching. */
.if-module { margin-top: var(--space-section, 96px); }

/* ==========================================================================
   MODULE STYLES
   ========================================================================== */

/* if- interfaces folder-tab, authored by Build per DESIGN.md §9 */
.if-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--space-unit, 4px) * 1);
}
.if-tab {
  border-radius: var(--radius-image, 8px) var(--radius-image, 8px) 0 0;
  background: var(--bg, #FAFAFA);
  color: var(--ink, #262626);
  font-family: var(--font-label, monospace);
  font-size: var(--text-xs, 0.8125rem);
  transition: background-color var(--duration-state, 220ms) ease-out, color var(--duration-state, 220ms) ease-out;
  padding: calc(var(--space-unit, 4px) * 2) calc(var(--space-unit, 4px) * 4.5);
  border: var(--border-thin, 1.5px) solid var(--ink, #262626);
  border-bottom: none;
}
.if-tab[aria-selected="true"] {
  background: var(--if-panel-ground, #F2F6F7);
  color: var(--ink, #262626);
}
.if-panels {
  background: var(--if-panel-ground, #F2F6F7);
  border-radius: 0 var(--radius-image, 8px) var(--radius-image, 8px) var(--radius-image, 8px);
  padding: calc(var(--space-unit, 4px) * 6);
}
.if-panel { display: grid; gap: var(--space-block, 24px); }
.if-panel[hidden] { display: none; }
.if-panel img { border-radius: var(--radius-image, 8px); }

/* No-JS fallback: if the tab script never runs, every panel renders stacked. */
.no-js .if-tabs { display: none; }
.no-js .if-panel[hidden] { display: grid; }
.no-js .if-panels { display: grid; gap: var(--space-section, 96px); background: none; padding: 0; }

/* --------------------------------------------------------------------------
   if-folder: straw folder skin for the if- module (design 6a).

   Everything is scoped to .if-folder, so hiii.html's tabs keep the flat
   Phase 1 treatment above and are untouched by this block.

   Clip geometry below stays literal on purpose. Those numbers are drawing
   coordinates that position two SVG illustrations against each other, not
   design-system values, and a token per coordinate would be abstraction for
   a problem that has not arrived (CLAUDE.md §1).
   -------------------------------------------------------------------------- */

.if-folder {
  padding: calc(var(--space-unit, 4px) * 2) 0 calc(var(--space-unit, 4px) * 7);
}

/* Tab row. Overrides the flat row above: tabs sit on the folder's top edge. */
.if-folder .if-tabs {
  align-items: flex-end;
  gap: calc(var(--space-unit, 4px) * 1.25);
  padding-left: calc(var(--space-unit, 4px) * 4);
  position: relative;
  z-index: 4;
}
.if-folder .if-tab {
  position: relative;
  z-index: 1;
  white-space: nowrap;
  cursor: pointer;
  border: none;
  border-radius: var(--radius-if-tab, 12px) var(--radius-if-tab, 12px) 2px 2px;
  padding: calc(var(--space-unit, 4px) * 3) calc(var(--space-unit, 4px) * 4);
  background-color: var(--if-straw-seated, #EAD68A);
  /* the reinforced strip across the top of a real folder tab */
  background-image: linear-gradient(var(--if-tab-reinforce, rgba(120, 95, 30, .16)) 0 6px, transparent 6px);
  box-shadow: var(--shadow-if-tab-seated, inset 0 -7px 9px -6px rgba(38, 38, 38, .28));
  transform: translateY(3px);
  transition:
    transform  var(--duration-if-tab, 340ms) var(--ease-if-spring, ease-out),
    padding    var(--duration-if-tab, 340ms) var(--ease-if-spring, ease-out),
    box-shadow var(--duration-if-tab, 340ms) ease-out,
    background-color var(--duration-if-fade, 260ms) ease-out;
}
.if-folder .if-tab[aria-selected="true"] {
  z-index: 5;
  /* Vertical padding only. Growing the tab horizontally too would reflow the
     flex row on every switch, sliding the untouched tabs sideways. */
  padding: calc(var(--space-unit, 4px) * 4) calc(var(--space-unit, 4px) * 4);
  background-color: var(--if-straw, #F7E6A0);
  box-shadow: var(--shadow-if-tab-pulled, 0 -6px 14px -4px rgba(38, 38, 38, .2));
  transform: translateY(-9px) rotate(-0.5deg);
}
.if-folder .if-tab:focus-visible {
  outline: var(--focus, 2px solid #262626);
  outline-offset: var(--focus-offset, 3px);
}

.if-folder .if-folder-body {
  position: relative;
  margin-top: -2px;   /* tabs overlap the jacket, so no seam shows */
}
.if-folder .if-folder-back {
  position: absolute;
  top: var(--if-back-offset, 8px);
  left: calc(var(--if-back-offset, 8px) + 1px);
  right: calc(var(--if-back-offset, 8px) * -1);
  bottom: calc(var(--if-back-offset, 8px) * -1);
  background: var(--if-straw-back, #DEC877);
  border-radius: 4px var(--radius-if-folder, 14px) var(--radius-if-folder, 14px) var(--radius-if-folder, 14px);
  z-index: 0;
}

/* .if-panels becomes the folder jacket. Overrides the flat rules above. */
.if-folder .if-panels {
  position: relative;
  z-index: 1;
  background: var(--if-straw, #F7E6A0);
  border-radius: 4px var(--radius-if-folder, 14px) var(--radius-if-folder, 14px) var(--radius-if-folder, 14px);
  padding: calc(var(--space-unit, 4px) * 2.5) calc(var(--space-unit, 4px) * 2.5) 0;
  box-shadow: var(--shadow-if-folder, 0 14px 26px -14px rgba(38, 38, 38, .4));
}
.if-folder .if-sheet {
  position: relative;
  display: grid;
  /* Bottom padding clears the pocket, which overlays the jacket's last 48px.
     Derived from the pocket token rather than hand-picked, so the two cannot
     drift apart: a hand-set 52px left the lowest tag chip 6px under the
     pocket on every hiii.html panel, against CLAUDE.md §9. */
  padding: calc(var(--space-unit, 4px) * 6) calc(var(--space-unit, 4px) * 5.5)
           calc(var(--if-pocket-height, 48px) + var(--space-block, 24px));
  background-color: var(--if-sheet, #FFFFFF);
  background-image: repeating-linear-gradient(
    transparent 0 calc(var(--if-sheet-rule-gap, 28px) - 1px),
    var(--if-sheet-rule, #EFE9D8) calc(var(--if-sheet-rule-gap, 28px) - 1px) var(--if-sheet-rule-gap, 28px));
  border-radius: var(--radius-if-sheet, 3px) var(--radius-if-sheet, 3px) 0 0;
  box-shadow: var(--shadow-if-sheet, inset 0 5px 10px -6px rgba(38, 38, 38, .45));
}
.if-folder .if-panel { gap: calc(var(--space-unit, 4px) * 5); }

/* The revealed panel rises onto the sheet. main.js toggles [hidden], so the
   panel goes display:none -> grid and the animation re-runs on every switch.
   It also plays once on load for the default panel, which is intended. */
.if-folder .if-panel:not([hidden]) {
  animation: if-sheet-in var(--duration-if-reveal, 300ms) var(--ease-if-reveal, ease-out);
}
@keyframes if-sheet-in {
  from { opacity: 0; transform: translateY(var(--if-reveal-rise, 10px)); }
  to   { opacity: 1; transform: none; }
}

/* Natural aspect ratio, never cropped (CLAUDE.md §9). The sheet grows and
   shrinks between tabs; the folder is anchored at the top, so it reads as
   a thicker or thinner sheet rather than a layout jump. */
.if-folder .if-panel img {
  width: 100%;
  height: auto;
  border-radius: 2px;
  border: var(--border-hair, 1px) solid var(--if-sheet-rule, #EFE9D8);
}

/* Slanted front pocket. The wrapper carries the drop-shadow so it follows the
   clipped slant instead of the element box, shading the fold where the sheet
   tucks in. */
.if-folder .if-pocket {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: var(--if-pocket-height, 48px);
  z-index: 3;
  pointer-events: none;
  filter: var(--shadow-if-pocket, drop-shadow(0 -3px 3px rgba(38, 38, 38, .3)));
}
.if-folder .if-pocket::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--if-straw-pocket, #F0DC8C);
  border-radius: 0 0 13px 13px;
  clip-path: polygon(0 0, 100% 38%, 100% 100%, 0 100%);
}

/* Clips. The gem clip's back loop is cropped at the paper edge by an
   overflow-hidden box, so it reads as passing behind the sheet. */
.if-folder .if-clip { position: absolute; pointer-events: none; }
.if-folder .if-clip path { vector-effect: non-scaling-stroke; }
.if-folder .if-clip-gem-back {
  top: -14px; right: 40px;
  width: 40px; height: 24px;
  overflow: hidden;
  z-index: 2;
}
.if-folder .if-clip-gem-back svg {
  position: absolute; top: 6px; left: 7px;
  width: 26px; height: 56px;
  transform: rotate(190deg);
  stroke: var(--if-brass-back, #A8893A);
}
.if-folder .if-clip-gem-front {
  top: -8px; right: 47px;
  width: 26px; height: 56px;
  transform: rotate(190deg);
  z-index: 2;
  stroke: var(--if-brass-front, #C9A94F);
  filter: drop-shadow(0 1px 1px rgba(38, 38, 38, 0.2));
}
.if-folder .if-clip-binder {
  left: 13%; bottom: 0;
  width: 38px; height: 42px;
  transform: translate(-50%, 38%) rotate(-6deg);
  z-index: 4;
  filter: drop-shadow(0 2px 2px rgba(38, 38, 38, 0.25));
}
.if-folder .if-clip-binder .clip-handle { stroke: var(--if-clip-steel, #9A9A9A); }
.if-folder .if-clip-binder .clip-body { fill: var(--ink, #262626); }
.if-folder .if-clip-binder .clip-shine { stroke: var(--if-clip-highlight, #4A4A4A); }

/* The tab still changes shape and color when selected, it just gets there
   instantly. Nothing here is the only signal for state. */
@media (prefers-reduced-motion: reduce) {
  .if-folder .if-tab { transition: none; }
  .if-folder .if-panel:not([hidden]) { animation: none; }
}

/* No-JS: the base rule hides the tabs and stacks every panel. The folder
   still wraps them; the gem clip would land on the wrong panel, so it goes. */
.no-js .if-folder .if-panels { background: var(--if-straw, #F7E6A0); padding: calc(var(--space-unit, 4px) * 2.5) calc(var(--space-unit, 4px) * 2.5) 0; }
.no-js .if-folder .if-sheet { gap: var(--space-section, 96px); }
.no-js .if-folder .if-clip-gem-back,
.no-js .if-folder .if-clip-gem-front { display: none; }

/* ==========================================================================
   UTILITIES
   ========================================================================== */

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

/* ==========================================================================
   MEDIA QUERIES
   ========================================================================== */

@media (max-width: 640px) {
  .identity-card-top { grid-template-columns: 1fr; }
  .identity-card-photo { aspect-ratio: 16 / 9; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0ms !important;
    animation-duration: 0ms !important;
  }
}
