/*
 * v2 home register.
 *
 * Ported from packages/design/study/huuuuue. Three things had to change on the
 * way in, and none of them are cosmetic:
 *
 * 1. No inline styles. style-src is 'self', which blocks style attributes too,
 *    so the marquee duration is a class here instead of --marquee-dur on the
 *    element.
 * 2. No display:grid outside study/ (packages/design/CLAUDE.md). The portfolio
 *    list is flex with flex-basis carrying the column share, the same way the
 *    stats row already did it upstream.
 * 3. Korean has no italic serif to fall back on, so .accent is a colour shift
 *    rather than the reference's Instrument Serif italic. Pretendard carries
 *    both registers; Archivo Black has no Hangul at all.
 */

/* Tokens live on :root, not on .v2: the header and menu sit outside that
   wrapper and still need the accent and the ink. */
:root {
    --v2-ink: #0a0a0a;
    --v2-paper: #fafafa;
    /* Yellow is a fill, not an ink: at 1.2:1 against the paper it cannot be
       read as text, so everywhere the accent used to BE the text, the text is
       now ink and the yellow is behind it or around it. --v2-on-accent is the
       colour anything sitting ON the yellow takes. */
    --v2-accent: #ffe600;
    --v2-on-accent: #0a0a0a;
    /* Used for a hover colour in the form. It was also the -webkit-text-stroke
       drawn around the display accent, which read as a border on every letter -
       the accent is the yellow itself, unoutlined, on every ground. */
    --v2-accent-edge: #8c6d00;
    --v2-smoke: #8a8a8a;

    --v2-paper-80: rgba(250, 250, 250, .8);
    --v2-paper-60: rgba(250, 250, 250, .6);
    --v2-paper-50: rgba(250, 250, 250, .5);
    --v2-paper-20: rgba(250, 250, 250, .2);
    --v2-paper-15: rgba(250, 250, 250, .15);
    --v2-ink-70: rgba(10, 10, 10, .7);
    --v2-ink-15: rgba(10, 10, 10, .15);

    /* Type. --v2-sans is the base: Pretendard first so Hangul is Pretendard
       everywhere, including outside the .v2 wrapper. The three faces below it
       carry Latin only - none of them has a Hangul glyph - so each is declared
       ahead of --v2-sans at the one place it belongs and Hangul falls through
       to Pretendard on its own. */
    --v2-sans: "Pretendard Variable", "Pretendard Fallback", -apple-system, BlinkMacSystemFont, 'Apple SD Gothic Neo', 'Malgun Gothic', 'Helvetica Neue', Arial, sans-serif;
    --v2-latin: "Inter";
    --v2-poster: "Archivo Black";
    --v2-serif: "Instrument Serif", Georgia, serif;

    --v2-pad: 40px;
    --v2-expo-out: cubic-bezier(.16, 1, .3, 1);
    --v2-expo-in-out: cubic-bezier(.87, 0, .13, 1);
    --v2-cue-ease: cubic-bezier(.65, 0, .35, 1);
    --v2-ease: cubic-bezier(.4, 0, .2, 1);

}

/* The page stops where the document stops.
 *
 * Scrolled to the end, the browser still moves the page a little further and
 * shows the canvas behind it - Chrome's elastic overscroll. The document is not
 * longer than the footer: scrollY stops at exactly scrollHeight - innerHeight
 * on every page at 1440x900, 1440x700, 1280x800, 1920x1080 and 390x844, and
 * forty more wheel notches do not move it. It is the bounce, not the content,
 * so the bounce is what is switched off.
 *
 * The gradient underneath is the second half: a page shorter than the viewport
 * leaves canvas below the footer with no bounce involved, and there the ink
 * half is what should show. Both are needed; painting the canvas alone only
 * changed the colour of the movement. */
html { overscroll-behavior-y: none; background-image: linear-gradient(to bottom, var(--v2-paper) 0 50%, var(--v2-ink) 50% 100%); background-attachment: fixed; }
/* And the footer is pushed to the bottom rather than left wherever the content
   ends: a page shorter than the viewport - the 404, or any page on a tall
   monitor - otherwise stops mid-screen and leaves canvas under it. */
/* The base stack sits on body, not on .v2: site.css puts the system stack there
   and anything outside the wrapper - the product detail pages, a bare 404 -
   inherited Hangul in Malgun Gothic. v2.css is linked after site.css, so an
   equal-specificity rule here wins. */
body { display: flex; flex-direction: column; min-height: 100svh; overscroll-behavior-y: none; font-family: var(--v2-latin), var(--v2-sans); }
body > main { flex: 1 0 auto; }

.v2 { background: var(--v2-paper); color: var(--v2-ink); }

/* The reference's own faces, self-hosted. Both are Google Fonts OFL, so these
   are the real thing rather than substitutes needing letter-spacing correction.
   Neither carries Hangul, so Korean falls through to Pretendard on its own. */
@font-face {
    font-family: "Archivo Black";
    src: url("/fonts/ArchivoBlack.woff2") format("woff2");
    font-weight: 400;
    font-display: swap;
}
@font-face {
    font-family: "Inter";
    src: url("/fonts/Inter.woff2") format("woff2");
    font-weight: 100 900;
    font-display: swap;
}
/* The reference's accent face. It ships a real italic; without it the browser
   synthesises the slant and every accent word sets to a different width. */
@font-face {
    font-family: "Instrument Serif";
    src: url("/fonts/InstrumentSerif.woff2") format("woff2");
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}
@font-face {
    font-family: "Instrument Serif";
    src: url("/fonts/InstrumentSerif-Italic.woff2") format("woff2");
    font-weight: 400;
    font-style: italic;
    font-display: swap;
}
/* The logotype only. Kept off --v2-poster so the wordmark stays distinct from
   the display headings it sits above - the mark's 45-degree cuts and this
   face's flat, geometric terminals read as one drawing. */
@font-face {
    font-family: "Space Grotesk";
    src: url("/fonts/SpaceGrotesk.woff2") format("woff2");
    font-weight: 700;
    font-display: swap;
}

/* Type primitives are NOT scoped to .v2: the header and the full-screen menu
   are siblings of it, not children, so a .v2-prefixed rule missed them and the
   logo rendered in the system stack at weight 400. */
.v2_display { font-family: var(--v2-poster), var(--v2-sans); font-weight: 400; text-transform: uppercase; letter-spacing: -0.02em; line-height: .92; }
/* Archivo Black is a single 400 weight that reads as a black; Pretendard at 400
   next to it reads as light. Anything set entirely in Korean takes the Pretendard
   weight that matches instead of inheriting a weight meant for the Latin face. */
.v2_display.is_ko { font-family: var(--v2-sans); font-weight: 800; letter-spacing: -0.03em; text-transform: none; }
.v2_eyebrow { font-size: 11px; font-weight: 600; line-height: 1.5; letter-spacing: 0.22em; text-transform: uppercase; }
/* The reference sets its accent word in Instrument Serif italic against the
   sans display - that contrast is the signature of the layout, not the colour.
   Kept uppercase here because the caps rule applies to all Latin display. */
/* No stroke. The accent is the yellow itself - an outline around every glyph
   reads as a border on the word, which is not what an accent is. */
.v2_accent { font-family: var(--v2-serif); font-style: italic; font-weight: 400; letter-spacing: -0.01em; color: var(--v2-accent); }
/* Instrument Serif carries no Hangul, so a Korean accent word falls through to
   a system serif and the browser synthesises the italic - a slanted, mismatched
   word next to Pretendard. In Korean the accent is the colour alone, set in the
   same face as the line it sits in. */
.v2_display.is_ko .v2_accent,
.v2_accent.is_ko { font-family: inherit; font-style: normal; font-weight: inherit; letter-spacing: inherit; }

/* --- Reset -------------------------------------------------------------- *
 * The study loads its own reset.css before huuuuue.css; that file was never
 * ported, so this register has been running on site.css's element rules. Two
 * were visible: every <ul> kept the UA's 40px padding-inline-start (only
 * list-style was switched off, one rule at a time), and site.css's
 * `p { margin: 0 0 14px }` put 14px under the footer wordmark.
 *
 * Specificity is the whole difficulty here, in both directions.
 *
 * The study can reset with bare `*` and `p` because its stylesheet has no
 * competition. Here site.css is an author sheet serving every other page, so a
 * `*` reset (0-0-0) loses to its `p { margin: 0 0 14px }` and would not fix the
 * wordmark - while an unscoped `p` reset would strip the margins off all eleven
 * pages. Hence :where() for the roots, which contributes nothing, and named
 * elements inside it: 0-0-1, tying site.css's element rules and winning on
 * order because v2.css loads second, and reaching nothing outside this register.
 *
 * Equally, the reset must NOT climb above 0-0-1. Scoping it as `.v2_header
 * button` (0-1-1) beat `.v2_eyebrow` and set the burger to 16px instead of
 * 11px; the study's `button { font: inherit }` is 0-0-1 and loses to its own
 * .eyebrow, which is what makes the reset safe to write at all.
 *
 * Only the parts of the study's reset this register uses - its ::selection,
 * .sr_only and hr rules have no counterpart here. */
:where(.v2, .v2_header, .v2_menu, .v2_footer)
  :is(h1, h2, h3, h4, h5, h6, p, ul, ol, li, dl, dt, dd, figure, blockquote, address, fieldset, legend, menu) { margin: 0; padding: 0; }

:where(.v2, .v2_header, .v2_menu, .v2_footer) :is(ul, ol) { list-style: none; }

/* The display register sizes every heading itself. Left to site.css these
   inherit a clamp() and weight 800 that has nothing to do with this layout. */
:where(.v2, .v2_header, .v2_menu, .v2_footer) :is(h1, h2, h3) { font-size: inherit; font-weight: inherit; line-height: inherit; }

/* The UA sheet sets small to `font-size: smaller`, which renders the footer's
   12px legal line at 10px. */
:where(.v2, .v2_header, .v2_menu, .v2_footer) small { font-size: inherit; }
:where(.v2, .v2_header, .v2_menu, .v2_footer) address { font-style: normal; }
:where(.v2, .v2_header, .v2_menu, .v2_footer) :is(button, input, textarea, select) { font: inherit; color: inherit; }
/* The rule above is deliberately zero-specificity, which site.css's `.field
   input` (0,1,1) outranks - the 접수 form's text inputs kept the system stack
   while the radios beside them were Pretendard. Same intent, enough weight. */
.v2 :is(button, input, textarea, select) { font-family: inherit; }

/* Body is Inter for Latin, Pretendard for Hangul - that pairing now lives on
   body as --v2-latin over --v2-sans, so the wrapper and its siblings inherit it
   and need no declaration of their own. site.css's note about keeping Pretendard
   to the display register to save subset requests no longer describes anything:
   every page already pulls all twelve Hangul subsets (308KB) for the prose in
   this register, so the base stack costs nothing further. */

/* Reveal contract, verbatim from the study: 32px of travel on a .7s expo-out,
   24px for the tighter blocks. Gated on .v2-motion so the copy is visible if
   the script never runs. The site's own .reveal/.is-in is deliberately not used
   here - its timing is different and the two together read as two pages. */
.v2-motion [data-reveal] { opacity: 0; transform: translateY(32px); transition: opacity .7s var(--v2-expo-out), transform .7s var(--v2-expo-out); }
.v2-motion [data-reveal="24"] { transform: translateY(24px); }
.v2-motion [data-reveal].is_inview { opacity: 1; transform: none; }

/* --- Intro curtain ------------------------------------------------------ */
/* The study runs this on html::before; the shell here is shared with every
   other page, so it is a real element that only the v2 body carries. */
.v2_curtain { position: fixed; inset: 0; z-index: 90; background: var(--v2-accent); transform: translateY(0); animation: v2_curtain .7s var(--v2-expo-in-out) .25s forwards; pointer-events: none; }

@keyframes v2_curtain { to { transform: translateY(-100%); } }

.v2 .v2_pill { display: inline-flex; align-items: center; line-height: 1.5; height: 52px; padding-inline: 32px; border-radius: 2em; background: var(--v2-ink); color: var(--v2-paper); font-size: 14px; font-weight: 600; letter-spacing: 0.18em; text-transform: uppercase; text-decoration: none; transition: background-color .3s var(--v2-ease); }
.v2 .v2_pill:hover { background: var(--v2-accent); color: var(--v2-on-accent); }
.v2 .v2_sweep { position: relative; text-decoration: none; color: inherit; }
.v2 .v2_sweep::after { content: ""; position: absolute; left: 0; bottom: -2px; width: 100%; height: 1px; background: var(--v2-accent); transform: scaleX(0); transform-origin: 100% 50%; transition: transform .4s var(--v2-cue-ease); }
.v2 .v2_sweep:hover::after { transform: scaleX(1); transform-origin: 0 50%; }

/* --- Header ------------------------------------------------------------- */
/* difference blending is why this never changes colour on scroll: white type
   over paper reads ink, over ink reads paper, with no scroll listener. It also
   means the mark has to be inline SVG on currentColor - an <img> would stay one
   colour. pointer-events are handed back to the children only, so the bar does
   not swallow clicks across the whole width. */
.v2_header { position: fixed; inset-inline: 0; top: 0; z-index: 50; display: flex; align-items: center; justify-content: space-between; padding: 20px 40px; color: #fff; mix-blend-mode: difference; pointer-events: none; }
/* The wordmark is set wide, not tight: letter-spacing is what separates a logo
   from a heading here, and the gaps between letters echo the space inside the
   mark. Its own face, so it does not shift when display type is retuned. */
.v2_header .v2_logo { display: flex; align-items: center; gap: 12px; font-family: "Space Grotesk", var(--v2-sans); font-weight: 700; font-size: 25px; line-height: 34px; letter-spacing: 0.08em; text-transform: uppercase; color: inherit; text-decoration: none; pointer-events: auto; }
.v2_header .v2_logo .mark { flex: none; }
.v2_header .v2_gnb a { font-size: 16px; letter-spacing: 0.14em; }
.v2_header .v2_burger { font-size: 16px; letter-spacing: 0.14em; }
.v2_header .v2_gnb { pointer-events: auto; }
.v2_header .v2_gnb ul { display: flex; align-items: center; gap: 32px; list-style: none; }
.v2_header .v2_gnb a { position: relative; display: block; color: inherit; text-decoration: none; }
.v2_header .v2_gnb a::after { content: ""; position: absolute; left: 0; bottom: -4px; width: 100%; height: 1px; background: currentColor; transform: scaleX(0); transform-origin: 100% 50%; transition: transform .3s cubic-bezier(.4, 0, .2, 1); }
.v2_header .v2_gnb a:hover::after { transform: scaleX(1); transform-origin: 0 50%; }
/* The burger is two rules drawn as pseudo elements, moved as a pair: 11px apart
   closed, crossed at the centre open. The label stays because motion.js rewrites
   the button's text between Menu. and Close. */
/* font-family: the same class collision as .note - site.css owns .bar__menu,
   which this button also carries for the menu contract, and sets it in the
   display register. The reset cannot reach a class, so it is said here. */
.v2_burger { position: relative; font-family: inherit; display: none; padding-right: 36px; background: none; border: 0; color: inherit; cursor: pointer; pointer-events: auto; }
.v2_burger::before,
.v2_burger::after { content: ""; position: absolute; right: 0; top: 50%; width: 24px; height: 1px; margin-top: -.5px; background: currentColor; transition: translate .3s cubic-bezier(.4, 0, .2, 1), rotate .3s cubic-bezier(.4, 0, .2, 1); }
.v2_burger::before { translate: 0 -5.5px; }
.v2_burger::after { translate: 0 5.5px; }
.v2_burger[aria-expanded="true"]::before { translate: 0 0; rotate: 45deg; }
.v2_burger[aria-expanded="true"]::after { translate: 0 0; rotate: -45deg; }

/* --- Full-screen menu --------------------------------------------------- */
/* The panel does not animate in; the rows arrive one after another out of their
   own overflow, so each travels exactly its own height. It sits under the
   header so the blended button stays legible without a second colour rule. */
.v2_menu { position: fixed; inset: 0; z-index: 40; display: none; flex-direction: column; justify-content: flex-end; padding: 96px 40px 48px; background: #0a0a0a; }
.v2_menu.is-open { display: flex; }
.v2_menu ul { list-style: none; }
.v2_menu li { overflow: hidden; border-bottom: 1px solid rgba(250, 250, 250, .15); }
.v2_menu li a { display: block; padding: 12px 0; font-size: 48px; line-height: 1; color: #fafafa; text-decoration: none; transition: color .2s cubic-bezier(.4, 0, .2, 1); }
.v2_menu li a:hover { color: #c06a2c; }
.v2_menu li:nth-child(1) a { --row: 0; }
.v2_menu li:nth-child(2) a { --row: 1; }
.v2_menu li:nth-child(3) a { --row: 2; }
.v2_menu li:nth-child(4) a { --row: 3; }
.v2_menu .cta { display: inline-block; margin-top: 40px; color: rgba(250, 250, 250, .5); }
.v2_menu .cta:hover { color: #fafafa; }
.v2-motion .v2_menu.is-open li a { animation: v2_menu_row .6s cubic-bezier(.16, 1, .3, 1) backwards; animation-delay: calc(110ms + var(--row) * 60ms); }

@keyframes v2_menu_row { from { transform: translateY(100%); } }

/* --- Hero --------------------------------------------------------------- */
.v2 .v2_hero { display: flex; flex-direction: column; justify-content: flex-end; position: relative; min-height: 100svh; padding: 128px var(--v2-pad) 48px; background: var(--v2-paper); }
.v2 .v2_hero .head { margin-bottom: 32px; color: var(--v2-smoke); }
/* 10vw, not the study's 11.5: measured, THE CHECKOUT needs 10.33vw at 768 and
   10.36vw at 390 to stay on one line, and a line that wraps breaks the effect -
   .line clips one row and slides it up, so a wrapped line rises as a block. */
.v2 .v2_hero .title { font-size: 10vw; line-height: .92; }
.v2 .v2_hero .line { display: block; overflow: hidden; }
.v2 .v2_hero .line > span { display: block; }
.v2-motion .v2 .v2_hero .line > span { transform: translateY(110%); animation: v2_line_in .9s var(--v2-expo-out) .85s forwards; }
.v2-motion .v2 .v2_hero .line:nth-child(2) > span { animation-delay: .97s; }
.v2 .v2_hero .foot { display: flex; align-items: flex-end; justify-content: space-between; margin-top: 56px; }
.v2 .v2_hero .lede { max-width: 448px; font-size: 18px; line-height: 1.625; color: var(--v2-ink-70); }
.v2 .v2_hero .actions { display: flex; align-items: center; gap: 24px; }
.v2 .v2_hero .cue { position: absolute; right: var(--v2-pad); top: 50%; display: flex; flex-direction: column; align-items: center; gap: 16px; transform: translateY(-50%); color: var(--v2-smoke); }
.v2 .v2_hero .cue span { writing-mode: vertical-rl; }
.v2 .v2_hero .cue::after { content: ""; width: 1px; height: 80px; background: var(--v2-ink-15); }
.v2 .v2_hero .cue::before { content: ""; position: absolute; left: 50%; bottom: 0; width: 1px; height: 80px; background: var(--v2-ink); animation: v2_cue 2s var(--v2-cue-ease) infinite; }

@keyframes v2_line_in { to { transform: none; } }
@keyframes v2_cue {
    from { clip-path: inset(0 0 66.7% 0); }
    to { clip-path: inset(66.7% 0 0 0); }
}

/* --- Sub-page head ------------------------------------------------------ */
/* The study's services_hero, which is how every page that is not the home
   opens: eyebrow, one clipped display line, a lede. It is shorter than the home
   hero on purpose - a sub-page has to reach its content, not hold a full
   screen before it. The .85s delay is the curtain's: the line rises as the
   curtain clears, same as the home. */
.v2 .v2_pagehead { padding: 176px var(--v2-pad) 80px; background: var(--v2-paper); }
.v2 .v2_pagehead .head { color: var(--v2-smoke); }
.v2 .v2_pagehead .title { margin-top: 24px; font-size: 10.5vw; line-height: .92; }
.v2 .v2_pagehead .line { display: block; overflow: hidden; }
.v2 .v2_pagehead .line > span { display: block; }
.v2-motion .v2 .v2_pagehead .line > span { transform: translateY(110%); animation: v2_line_in .9s var(--v2-expo-out) .85s forwards; }
.v2-motion .v2 .v2_pagehead .line:nth-child(2) > span { animation-delay: .97s; }
.v2-motion .v2 .v2_pagehead .line:nth-child(3) > span { animation-delay: 1.09s; }
.v2 .v2_pagehead .lede { max-width: 576px; margin-top: 32px; font-size: 18px; line-height: 1.625; color: var(--v2-ink-70); }

/* --- Marquee ------------------------------------------------------------ */
/* Exactly two identical rows travelling -50%, so the seam is invisible. One row
   or three and the loop jumps. */
.v2 .v2_marquee { overflow: hidden; }
.v2 .v2_marquee .track { display: flex; align-items: center; width: max-content; animation: v2_marquee 28s linear infinite; }
.v2 .v2_marquee .row { display: flex; flex-shrink: 0; align-items: center; }

@keyframes v2_marquee {
    from { transform: translateX(0); }
    to { transform: translateX(-50%); }
}

/* --- Services ----------------------------------------------------------- */
.v2 .v2_services { background: var(--v2-ink); color: var(--v2-paper); }
.v2 .v2_services .v2_marquee { border-top: 1px solid var(--v2-paper-15); padding-block: 24px; }
.v2 .v2_services .v2_marquee .row > span { display: flex; align-items: center; margin-inline: 32px; font-size: 24px; line-height: 32px; color: var(--v2-paper-80); }
.v2 .v2_services .v2_marquee .row > span::after { content: ""; width: 8px; height: 8px; margin-left: 64px; border-radius: 50%; background: var(--v2-accent); }
.v2 .v2_services .body { padding: 144px var(--v2-pad); }
.v2 .v2_services .head { color: var(--v2-paper-50); }
.v2 .v2_services .title { max-width: 896px; margin-top: 16px; font-size: 72px; line-height: 1; }
.v2 .v2_services .list { margin-top: 64px; list-style: none; }
.v2 .v2_services .list a { display: flex; align-items: baseline; justify-content: space-between; gap: 24px; padding-block: 24px; border-top: 1px solid var(--v2-paper-20); color: inherit; text-decoration: none; transition: color .3s var(--v2-ease); }
.v2 .v2_services .list a:hover { color: var(--v2-accent); }
.v2 .v2_services .label { display: flex; align-items: baseline; gap: 24px; }
.v2 .v2_services .num { color: var(--v2-accent); }
.v2 .v2_services .names { display: flex; align-items: baseline; gap: 16px; flex-wrap: wrap; min-width: 0; }
.v2 .v2_services .name { font-size: 48px; line-height: 1; }
/* The English word is the display line; the Korean under it is what a shop
   owner is actually scanning for, so it stays legible rather than decorative. */
.v2 .v2_services .ko { font-size: 15px; line-height: 1.5; color: var(--v2-paper-50); }
.v2 .v2_services .arrow { font-size: 24px; line-height: 1.3333; transition: transform .3s var(--v2-ease); }
.v2 .v2_services .list a:hover .arrow { transform: translateX(8px); }

/* --- Manifesto ----------------------------------------------------------- */
/* The study's set piece: one paragraph that lights word by word as it passes,
   scrubbed against scroll rather than triggered. v2.js splits it on spaces, so
   every span is one 어절 and the line still breaks where Korean is allowed to
   break - splitting per character is what tears Korean apart, and nothing here
   does that.

   1.15 leading is the study's, set for a Latin face whose caps sit inside the
   em box. A Hangul syllable fills it, so the same figure overlaps: 1.35 here,
   measured against the same paragraph. */
.v2 .v2_manifesto { padding: 128px var(--v2-pad); background: var(--v2-ink); color: var(--v2-paper); }
.v2 .v2_manifesto .head { color: var(--v2-paper-50); }
.v2 .v2_manifesto .cols { display: flex; flex-wrap: wrap; gap: 48px; align-items: flex-start; }
.v2 .v2_manifesto .text { flex: 1 1 100%; min-width: 0; }
.v2 .v2_manifesto .body { max-width: 1024px; margin-top: 40px; font-size: 28px; line-height: 1.45; letter-spacing: -0.02em; word-break: keep-all; }
.v2 .v2_manifesto .support { max-width: 52ch; margin-top: 32px; font-size: 16px; line-height: 1.8; color: var(--v2-paper-60); word-break: keep-all; }
/* The photo is a block of ground, not a portrait: it takes the column's width
   and a fixed ratio, and the crop keeps the top where the screens are. */
.v2 .v2_manifesto .shot { display: block; position: relative; flex: 1 1 100%; aspect-ratio: 4 / 3; overflow: hidden; }
.v2 .v2_manifesto .shot img { position: absolute; inset: 0; display: block; width: 100%; height: 100%; object-fit: cover; }
/* Resting state belongs to the script, not here: with no JavaScript every word
   has to read at full strength rather than sitting at 14% for good. */
.v2-motion .v2 .v2_manifesto .body span { opacity: .14; }

/* --- Rules --------------------------------------------------------------- */
.v2 .v2_rules { padding: 96px var(--v2-pad); background: var(--v2-paper); }
.v2 .v2_rules .head { font-size: 48px; line-height: 1.22; }
.v2 .v2_rules .list { display: flex; flex-wrap: wrap; gap: 48px; margin-top: 56px; }
.v2 .v2_rules .rule { flex: 1 1 100%; padding-top: 20px; border-top: 1px solid var(--v2-ink-15); }
.v2 .v2_rules h3 { font-size: 24px; line-height: 1.3; }
.v2 .v2_rules p { max-width: 65ch; margin-top: 12px; line-height: 1.625; color: var(--v2-ink-70); word-break: keep-all; }
/* The noun phrases under each rule. Not links and not buttons - they are the
   half of the explanation that does not need a sentence around it. */
.v2 .v2_rules .chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; list-style: none; }
.v2 .v2_rules .chips li { padding: 6px 14px; border: 1px solid var(--v2-ink-15); border-radius: 2em; font-size: 13px; line-height: 1.5; color: var(--v2-ink-70); }

/* A list of steps rather than links: the rows carry no href, so they take none
   of the hover and none of the arrow. */
/* A list of steps rather than links: the rows carry no href, so they take none
   of the hover and none of the arrow. Each row is a head and a paragraph. */
.v2 .v2_services .list.is_static > li { display: flex; flex-direction: column; gap: 12px; padding-block: 28px; border-top: 1px solid var(--v2-paper-20); }
.v2 .v2_services .list.is_static .head { display: flex; align-items: baseline; gap: 24px; }
.v2 .v2_services .list.is_static .detail { max-width: 70ch; font-size: 16px; line-height: 1.75; color: var(--v2-paper-60); word-break: keep-all; }

/* --- Portfolio ---------------------------------------------------------- */
/* The study's about_team grid, rebuilt in flex. flex-basis carries the column
   share and the gap comes out of it, the same trick the stats row uses. */
.v2 .v2_portfolio { padding: 96px var(--v2-pad); background: var(--v2-ink); color: var(--v2-paper); }
.v2 .v2_portfolio .head h2 { font-size: 64px; line-height: 1; }
.v2 .v2_portfolio .head .v2_eyebrow { color: var(--v2-paper-50); margin-bottom: 12px; }
.v2 .v2_portfolio .list { display: flex; flex-wrap: wrap; gap: 56px 24px; margin-top: 56px; list-style: none; }
.v2 .v2_portfolio .list > li { flex: 0 1 calc(50% - 12px); min-width: 0; }
/* site.css underlines links site-wide; the card is one big link, so the rule
   has to be undone here or every line in the card gets a rule under it. */
.v2 .v2_portfolio .list a { display: block; color: inherit; text-decoration: none; }
/* The ratio lives on the container, not the image: site.css sets `height:auto`
   on img, which beats aspect-ratio there and left each card a different height.
   16:10 rather than the study's square - these are 16:9 screen captures, and a
   square crop threw away 44% of every one of them. object-position keeps the
   top of the page, which is the part that identifies the work. */
.v2 .v2_portfolio .shot { display: block; position: relative; aspect-ratio: 16 / 10; overflow: hidden; border: 1px solid var(--v2-paper-15); }
.v2 .v2_portfolio .shot img { position: absolute; inset: 0; display: block; width: 100%; height: 100%; object-fit: cover; object-position: top center; transition: transform .7s var(--v2-expo-out); }
.v2 .v2_portfolio li:hover .shot img { transform: scale(1.05); }
.v2 .v2_portfolio h3 { margin-top: 20px; font-size: 20px; }
.v2 .v2_portfolio .role { margin-top: 6px; color: var(--v2-accent); }
.v2 .v2_portfolio .bio { margin-top: 10px; font-size: 14px; line-height: 1.6; color: var(--v2-paper-60); }


/* The contact page borrows the form's two-column shell and fills the left
   column with the ways to reach us instead of a second form. */
.v2 .v2_form .ways { display: flex; flex: 1 1 100%; flex-direction: column; gap: 0; list-style: none; }
.v2 .v2_form .ways > li { display: flex; flex-direction: column; gap: 8px; padding-block: 28px; border-bottom: 1px solid var(--v2-ink-15); }
.v2 .v2_form .ways .label { color: var(--v2-smoke); }
.v2 .v2_form .ways a { font-size: 28px; line-height: 1.2; color: var(--v2-ink); text-decoration: none; transition: color .3s var(--v2-ease); }
.v2 .v2_form .ways a:hover { color: var(--v2-accent-edge); }
.v2 .v2_form .aside .foot { margin-top: 8px; }
.v2 .v2_form .aside .foot a { color: var(--v2-ink); text-decoration: none; }

/* --- Contact hero -------------------------------------------------------- */
/* study/huuuuue contact.html: one line, larger than the other two heroes -
   18vw below 768 and 13vw above. */
.v2 .v2_contact_hero { padding: 144px var(--v2-pad) 64px; background: var(--v2-paper); }
.v2 .v2_contact_hero .head { color: var(--v2-smoke); }
/* SEND IT OVER. on /setup fits to 11.0vw at 360; /contact shares the rule */
.v2 .v2_contact_hero .title { margin-top: 24px; font-size: 10.8vw; line-height: .92; }
.v2 .v2_contact_hero .line { display: block; overflow: hidden; }
.v2 .v2_contact_hero .line > span { display: block; }
.v2-motion .v2 .v2_contact_hero .line > span { transform: translateY(110%); animation: v2_line_in .9s var(--v2-expo-out) .85s forwards; }
.v2 .v2_contact_hero .lede { max-width: 576px; margin-top: 32px; font-size: 18px; line-height: 1.7; color: var(--v2-ink-70); word-break: keep-all; }

/* --- Form ---------------------------------------------------------------- */
/* The study's contact page: the brief on the left, ruled notes on the right.
   Its 1.2fr/1fr split is a grid there; here it is flex with the ratio carried
   by flex-basis, the same substitution the portfolio list makes. */
.v2 .v2_form { padding: 0 var(--v2-pad) 128px; background: var(--v2-paper); }
.v2 .v2_form .cols { display: flex; flex-wrap: wrap; gap: 64px; }
.v2 .v2_form .brief { display: flex; flex: 1 1 100%; flex-direction: column; gap: 40px; }
.v2 .v2_form .field { display: flex; flex-direction: column; }
/* The UA's [hidden] rule is 0-1-0 and loses to the class above, so a
   hidden .field stayed on screen while a hidden plain div went away. */
.v2 .v2_form [hidden] { display: none; }
/* site.css owns .field > label, .choices label and .consent and sets all three
   in the mono register with 0.14em tracking - the same class collision .note
   and .bar__menu already hit. An element reset cannot reach a class, so each
   one has to say `font-family: inherit` where it is declared. */
.v2 .v2_form label,
.v2 .v2_form .label { font-family: inherit; letter-spacing: normal; text-transform: none; color: var(--v2-smoke); font-size: 14px; line-height: 1.5; }
.v2 .v2_form .req { color: var(--v2-ink); }
.v2 .v2_form input[type="text"],
.v2 .v2_form input[type="url"],
.v2 .v2_form input[type="tel"],
.v2 .v2_form input[type="email"],
.v2 .v2_form input[type="password"],
.v2 .v2_form textarea { width: 100%; margin-top: 12px; padding-bottom: 12px; border: 0; border-bottom: 1px solid var(--v2-ink-15); background: none; font-size: 20px; color: var(--v2-ink); transition: border-color .3s var(--v2-ease); }
.v2 .v2_form textarea { resize: vertical; }
.v2 .v2_form input::placeholder,
.v2 .v2_form textarea::placeholder { color: var(--v2-ink-15); }
.v2 .v2_form input:focus,
.v2 .v2_form textarea:focus { border-bottom-color: var(--v2-ink); outline: 0; }
/* Radios and checkboxes read as pills. The control itself stays in the markup
   and only moves out of sight: replacing it with a styled span would take the
   keyboard and the screen reader with it. */
.v2 .v2_form .choices { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.v2 .v2_form .choices label { font-family: inherit; font-size: 15px; letter-spacing: normal; display: inline-flex; align-items: center; padding: 8px 16px; border: 1px solid var(--v2-ink-15); border-radius: 2em; color: var(--v2-ink); cursor: pointer; transition: border-color .2s var(--v2-ease), background-color .2s var(--v2-ease), color .2s var(--v2-ease); }
.v2 .v2_form .choices label:hover { border-color: var(--v2-ink); }
.v2 .v2_form .choices input { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.v2 .v2_form .choices label:has(input:checked) { border-color: var(--v2-ink); background: var(--v2-ink); color: var(--v2-paper); }
.v2 .v2_form .choices label:has(input:focus-visible) { outline: 2px solid var(--v2-ink); outline-offset: 2px; }
.v2 .v2_form .hint { margin-top: 8px; font-size: 13px; line-height: 1.6; color: var(--v2-smoke); }
.v2 .v2_form .msg { margin-top: 8px; padding-left: 10px; border-left: 3px solid var(--v2-accent); font-size: 13px; line-height: 1.6; color: var(--v2-ink); }
.v2 .v2_form .field.bad input,
.v2 .v2_form .field.bad textarea { border-bottom-color: var(--v2-ink); }
/* The order sheet asks for close to thirty things. artfinger's does too, and it
   is readable because it is split into three named blocks rather than one run
   of inputs - this is that rule, drawn with the eyebrow the register uses for
   every other section label. */
.v2 .v2_form .group_head { padding-top: 20px; border-top: 1px solid var(--v2-ink); }
.v2 .v2_form .group_head .v2_eyebrow { color: var(--v2-ink); }
.v2 .v2_form .group_head .hint { max-width: 48ch; }
.v2 .v2_form .err { padding: 16px 20px; border-left: 6px solid var(--v2-accent); background: rgba(255, 230, 0, .18); color: var(--v2-ink); line-height: 1.6; }
.v2 .v2_form .consent { display: flex; align-items: flex-start; gap: 12px; color: var(--v2-ink-70); cursor: pointer; }
.v2 .v2_form .consent input { flex: none; margin-top: 4px; accent-color: var(--v2-accent); }
.v2 .v2_form .consent a { color: inherit; }
.v2 .v2_form .send { align-self: flex-start; height: 56px; padding-inline: 40px; border: 0; cursor: pointer; }
.v2 .v2_form .aside { display: flex; flex: 1 1 100%; flex-direction: column; gap: 48px; }
.v2 .v2_form .aside .block { padding-top: 20px; border-top: 1px solid var(--v2-ink-15); }
.v2 .v2_form .aside .v2_eyebrow { color: var(--v2-smoke); }
.v2 .v2_form .aside .block p { margin-top: 8px; max-width: 40ch; line-height: 1.625; color: var(--v2-ink-70); word-break: keep-all; }
/* /intro closes its rules block with a button under it. */
.v2 .v2_rules .foot { margin-top: 56px; }

/* --- Board --------------------------------------------------------------- */
/* A list of submissions is a table: five columns of the same kind of fact per
   row, with a header that names them. Nothing here is a layout table - the
   register's flex lists carry the rest of the site, and this is the one place
   where rows and columns are the content. */
.v2 .v2_board { padding: 0 var(--v2-pad) 128px; background: var(--v2-paper); }
.v2 .v2_board .table_wrap { overflow-x: auto; }
.v2 .v2_board table { width: 100%; border-collapse: collapse; }
.v2 .v2_board caption { padding-bottom: 16px; color: var(--v2-smoke); text-align: left; }
.v2 .v2_board th { padding: 16px 12px; border-top: 1px solid var(--v2-ink); border-bottom: 1px solid var(--v2-ink-15); font-size: 13px; font-weight: 500; color: var(--v2-smoke); text-align: left; }
.v2 .v2_board td { padding: 20px 12px; border-bottom: 1px solid var(--v2-ink-15); font-size: 15px; color: var(--v2-ink-70); }
.v2 .v2_board .no,
.v2 .v2_board .who,
.v2 .v2_board .when,
.v2 .v2_board .state { white-space: nowrap; }
.v2 .v2_board td.subject { width: 100%; }
.v2 .v2_board td.subject a { font-size: 18px; color: var(--v2-ink); text-decoration: none; transition: color .3s var(--v2-ease); }
.v2 .v2_board td.subject a:hover { color: var(--v2-accent-edge); }
.v2 .v2_board td.empty { padding-block: 64px; color: var(--v2-smoke); text-align: center; }
.v2 .v2_board .pager { display: flex; align-items: center; gap: 24px; margin-top: 40px; }
.v2 .v2_board .pager .count { color: var(--v2-smoke); }
.v2 .v2_board .foot { display: flex; align-items: center; gap: 24px; margin-top: 56px; }
.v2 .v2_board .foot a.v2_sweep { color: var(--v2-ink); text-decoration: none; }
/* An opened post. The rows are a description list because that is what they
   are - a label and the value somebody typed against it. */
.v2 .v2_board .detail { display: flex; flex-direction: column; border-top: 1px solid var(--v2-ink); }
.v2 .v2_board .detail .row { display: flex; flex-wrap: wrap; gap: 8px 24px; padding-block: 16px; border-bottom: 1px solid var(--v2-ink-15); }
.v2 .v2_board .detail dt { flex: 0 0 160px; color: var(--v2-smoke); }
/* pre-line, because the SNS and category fields ask for one entry per line and
   without it the list arrives as one run-on string. */
.v2 .v2_board .detail dd { flex: 1 1 240px; line-height: 1.625; color: var(--v2-ink); word-break: break-word; white-space: pre-line; }
.v2 .v2_board .message,
.v2 .v2_board .reply { margin-top: 48px; padding-top: 20px; border-top: 1px solid var(--v2-ink-15); }
.v2 .v2_board .message p + p,
.v2 .v2_board .reply p + p { margin-top: 12px; line-height: 1.7; color: var(--v2-ink-70); word-break: keep-all; }
/* The post's own title, moved out of the page head. One step above body size -
   large enough to be the heading of what follows, small enough that a sentence
   a visitor typed does not become a billboard. */
.v2 .post_head { margin-bottom: 40px; }
.v2 .post_head .post_title { margin-top: 10px; font-size: 22px; font-weight: 700; line-height: 1.45; letter-spacing: -0.02em; color: var(--v2-ink); word-break: keep-all; }
.v2 .v2_board.is_post { padding-bottom: 0; }
.v2 .v2_board.foot_nav { padding-top: 0; }

/* --- Drawn marks --------------------------------------------------------- */
/* The icons are hand-drawn SVG, so a viewBox unit is not a real size the way a
   1:1 export from a design tool would be - the box is sized here on purpose.
   28px sits between the 20px heading and the 13px chips under it. */
/* On the ink sections the yellow stands on its own. On paper it needs a ground,
   so the icon sits in an ink tile - which is the mark's own shape anyway. */
.v2 .v2_icon { display: block; margin-bottom: 16px; color: var(--v2-accent); }
.v2 .v2_rules .v2_icon { box-sizing: content-box; padding: 6px; background: var(--v2-ink); }

/* The mark at band scale, the one graphic on the page that is not type. The
   drawing tiles rather than stretches, so the height is a crop of the pattern
   and is set here - the SVG has no intrinsic size to inherit. */
.v2 .v2_band { padding: 0 var(--v2-pad) 96px; background: var(--v2-paper); color: var(--v2-ink-15); }
.v2 .v2_band_art { display: block; width: 100%; height: 60px; }

/* --- Photo strips -------------------------------------------------------- */
/* Full-measure photographs, cropped to a letterbox so they read as a band
   between sections rather than as a picture someone placed. Sources and
   licence in public/img/site/CREDITS.md. */
.v2_strip { display: block; position: relative; overflow: hidden; height: 220px; background: var(--v2-ink); }
.v2_strip.is_tall { height: 320px; }
.v2_strip img { position: absolute; inset: 0; display: block; width: 100%; height: 100%; object-fit: cover; object-position: center; }
/* Greyscale, on every photograph on the site. Three stock shots arrive with
   three unrelated colour casts - a pink 3D render, a warm desk, a blue mall -
   and next to a two-colour ink-and-yellow system each one reads as a picture
   somebody dropped in. Stripped of their own colour they become ground, and the
   yellow stays the only colour on the page. */
.v2_strip img,
.v2 .v2_manifesto .shot img { filter: grayscale(1) contrast(1.06); }

/* --- Icon motion --------------------------------------------------------- */
/* One moving part each, and only one: eight icons all animating at once on the
   same screen is a fairground. Everything is transform or opacity, both of
   which the compositor can run without touching layout, and every keyframe
   spends most of its cycle at rest so the page is still when it is being read.
   transform-box: fill-box makes a percentage transform-origin mean the shape's
   own box rather than the SVG viewport's. */
.v2 .v2_icon [class^="v2_"] { transform-box: fill-box; }
.v2 .v2_icon .v2_slide { animation: v2_i_slide 5s var(--v2-ease) infinite; }
.v2 .v2_icon .v2_spin { transform-origin: 50% 50%; animation: v2_i_spin 6s linear infinite; }
.v2 .v2_icon .v2_strike { animation: v2_i_strike 4s var(--v2-ease) infinite; }
.v2 .v2_icon .v2_step { animation: v2_i_step 3s steps(1, end) infinite; }
.v2 .v2_icon .v2_step_2 { animation-delay: .4s; }
.v2 .v2_icon .v2_step_3 { animation-delay: .8s; }
.v2 .v2_icon .v2_wipe { animation: v2_i_wipe 5s var(--v2-ease) infinite; }
.v2 .v2_icon .v2_lift { transform-origin: 50% 50%; animation: v2_i_lift 5s var(--v2-ease) infinite; }
.v2 .v2_icon .v2_pulse { transform-origin: 50% 50%; animation: v2_i_pulse 3s var(--v2-ease) infinite; }
.v2 .v2_icon .v2_swap { animation: v2_i_swap 4s var(--v2-ease) infinite; }

@keyframes v2_i_slide { 0%, 30% { transform: none; } 55%, 85% { transform: translateX(9px); } 100% { transform: none; } }
@keyframes v2_i_spin { to { transform: rotate(360deg); } }
@keyframes v2_i_strike { 0%, 15% { stroke-dashoffset: 9; } 45%, 100% { stroke-dashoffset: 0; } }
@keyframes v2_i_step { 0%, 32% { opacity: .25; } 33%, 100% { opacity: 1; } }
@keyframes v2_i_wipe { 0%, 25% { transform: none; } 55%, 80% { transform: translateX(7px); } 100% { transform: none; } }
@keyframes v2_i_lift { 0%, 35% { transform: none; } 60%, 85% { transform: translate(-1.5px, -1.5px); } 100% { transform: none; } }
@keyframes v2_i_pulse { 0%, 40% { transform: scale(1); } 60% { transform: scale(1.25); } 100% { transform: scale(1); } }
@keyframes v2_i_swap { 0%, 30% { opacity: 1; } 50% { opacity: .2; } 70%, 100% { opacity: 1; } }

/* --- About hero ---------------------------------------------------------- */
/* study/huuuuue about.html. 15vw below 768 and 10.5vw above it, as the study
   sets them; the lines clip and rise on load. */
.v2 .v2_about_hero { padding: 144px var(--v2-pad) 80px; background: var(--v2-paper); }
.v2 .v2_about_hero .head { color: var(--v2-smoke); }
/* TESTED TO THE fits to 10.2vw at 360 */
.v2 .v2_about_hero .title { margin-top: 24px; font-size: 10vw; line-height: .92; }
.v2 .v2_about_hero .line { display: block; overflow: hidden; }
.v2 .v2_about_hero .line > span { display: block; }
.v2-motion .v2 .v2_about_hero .line > span { transform: translateY(110%); animation: v2_line_in .9s var(--v2-expo-out) .85s forwards; }
.v2-motion .v2 .v2_about_hero .line:nth-child(2) > span { animation-delay: .97s; }
.v2-motion .v2 .v2_about_hero .line:nth-child(3) > span { animation-delay: 1.09s; }

/* --- Team grid, carrying products ---------------------------------------- */
/* The study's about_team: a grid of cards whose image lifts on hover. Two up,
   three above 768, and flex rather than grid as everywhere here. */
.v2 .v2_team { padding: 96px var(--v2-pad); background: var(--v2-ink); color: var(--v2-paper); }
.v2 .v2_team .head .v2_eyebrow { color: var(--v2-paper-50); }
.v2 .v2_team .head h2 { margin-top: 16px; font-size: 48px; line-height: 1.1; }
.v2 .v2_team .list { display: flex; flex-wrap: wrap; gap: 56px 24px; margin-top: 56px; list-style: none; }
.v2 .v2_team .list > li { flex: 1 1 calc(50% - 12px); min-width: 0; }
.v2 .v2_team a { display: block; color: inherit; text-decoration: none; }
.v2 .v2_team .shot { display: block; position: relative; aspect-ratio: 16 / 10; overflow: hidden; border: 1px solid var(--v2-paper-15); background: var(--v2-ink); }
.v2 .v2_team .shot img { position: absolute; inset: 0; display: block; width: 100%; height: 100%; object-fit: cover; object-position: top center; transition: transform .7s var(--v2-expo-out); }
.v2 .v2_team li:hover .shot img { transform: scale(1.05); }
.v2 .v2_team h3 { margin-top: 16px; font-size: 22px; line-height: 1.3; }
.v2 .v2_team .role { margin-top: 6px; color: var(--v2-accent); }
.v2 .v2_team .bio { margin-top: 10px; font-size: 14px; line-height: 1.6; color: var(--v2-paper-60); word-break: keep-all; }

/* --- Refusals ------------------------------------------------------------ */
/* The study's about_nos: a strip of the things it will not do, travelling on
   the accent. Ours are not attitude - each line is something declined, and the
   rules section above says why for four of them. */
.v2 .v2_nos { overflow-x: clip; padding-block: 20px; background: var(--v2-accent); color: var(--v2-on-accent); }
.v2 .v2_nos .track { display: flex; width: max-content; align-items: center; animation: v2_marquee 24s linear infinite; }
.v2 .v2_nos .track > span { display: inline-flex; align-items: center; margin-inline: 32px; font-size: 20px; line-height: 1.4; }
.v2 .v2_nos .track > span > span { margin-inline: 24px; opacity: .55; }

/* --- Refusals ------------------------------------------------------------ */
/* The study's about_nos: a strip of the things it will not do, travelling on
   the accent. Ours are not attitude - each line is something declined, and the
   rules section above says why for four of them. */
.v2 .v2_nos { overflow-x: clip; padding-block: 20px; background: var(--v2-accent); color: var(--v2-on-accent); }
.v2 .v2_nos .row > span { display: inline-flex; align-items: center; margin-inline: 32px; font-size: 18px; line-height: 1.4; }
.v2 .v2_nos .row > span::before { content: "✕"; margin-right: 12px; opacity: .6; }

/* --- Column grid --------------------------------------------------------- */
/* Four groups of short lines. The study runs its equivalent as a grid; here the
   column share rides on flex-basis, gaps subtracted, as everywhere else. */
.v2 .v2_grid { padding: 96px var(--v2-pad); background: var(--v2-ink); color: var(--v2-paper); }
.v2 .v2_grid .head .v2_eyebrow { color: var(--v2-paper-50); margin-bottom: 12px; }
.v2 .v2_grid .head h2 { font-size: 48px; line-height: 1; }
.v2 .v2_grid > .list { display: flex; flex-wrap: wrap; gap: 48px 24px; margin-top: 56px; list-style: none; }
.v2 .v2_grid > .list > li { flex: 1 1 100%; padding-top: 20px; border-top: 1px solid var(--v2-paper-20); }
.v2 .v2_grid h3 { font-size: 20px; line-height: 1.3; }
.v2 .v2_grid .argument { margin-top: 12px; font-size: 15px; line-height: 1.7; color: var(--v2-paper-60); word-break: keep-all; }
.v2 .v2_grid ul { margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--v2-paper-15); list-style: none; }
.v2 .v2_grid ul li { padding-block: 6px; font-size: 15px; line-height: 1.6; color: var(--v2-paper-60); word-break: keep-all; }
.v2 .v2_grid .fields { display: flex; flex-wrap: wrap; align-items: baseline; gap: 12px 24px; margin-top: 32px; padding-top: 24px; border-top: 1px solid var(--v2-paper-20); }
.v2 .v2_grid .fields:first-of-type { margin-top: 56px; }
.v2 .v2_grid .fields .v2_eyebrow { color: var(--v2-paper-50); }
.v2 .v2_grid .fields .v2_display { font-size: 28px; line-height: 1.2; }

/* --- Services hero and stack --------------------------------------------- */
/* study/huuuuue services.html. Each card sticks 12px lower than the one before
   it, so the pile builds against the top of the viewport as you scroll and
   every card keeps a sliver of the one behind it showing. */
.v2 .v2_svc_hero { padding: 144px var(--v2-pad) 80px; background: var(--v2-paper); }
.v2 .v2_svc_hero .head { color: var(--v2-smoke); }
/* TO CHECKOUT. fits to 10.8vw at 360 */
.v2 .v2_svc_hero .title { margin-top: 24px; font-size: 10.5vw; line-height: .92; }
.v2 .v2_svc_hero .line { display: block; overflow: hidden; }
.v2 .v2_svc_hero .line > span { display: block; }
.v2-motion .v2 .v2_svc_hero .line > span { transform: translateY(110%); animation: v2_line_in .9s var(--v2-expo-out) .85s forwards; }
.v2-motion .v2 .v2_svc_hero .line:nth-child(2) > span { animation-delay: .97s; }
.v2 .v2_svc_hero .lede { max-width: 576px; margin-top: 32px; font-size: 18px; line-height: 1.7; color: var(--v2-ink-70); word-break: keep-all; }

.v2 .v2_stack { padding: 0 var(--v2-pad) 128px; background: var(--v2-paper); }
.v2 .v2_stack .card { position: sticky; top: 96px; }
.v2 .v2_stack .card:nth-child(2) { top: 108px; }
.v2 .v2_stack .card:nth-child(3) { top: 120px; }
.v2 .v2_stack .card:nth-child(4) { top: 132px; }
.v2 .v2_stack .inner { margin-bottom: 32px; padding: 40px 24px; border: 1px solid var(--v2-ink-15); background: var(--v2-paper); color: var(--v2-ink); }
.v2 .v2_stack .card:nth-child(even) .inner { border-color: var(--v2-paper-20); background: var(--v2-ink); color: var(--v2-paper); }
.v2 .v2_stack .meta { display: flex; align-items: baseline; justify-content: space-between; gap: 24px; }
.v2 .v2_stack .num { color: var(--v2-accent); }
.v2 .v2_stack .kind { opacity: .5; }
.v2 .v2_stack h2 { margin-top: 24px; font-size: 44px; line-height: 1; }
.v2 .v2_stack .body { display: flex; flex-direction: column; gap: 32px; margin-top: 32px; }
.v2 .v2_stack .body p { max-width: 65ch; font-size: 16px; line-height: 1.7; opacity: .8; word-break: keep-all; }
.v2 .v2_stack .body ul { list-style: none; }
.v2 .v2_stack .body li { display: flex; align-items: center; justify-content: space-between; padding: 10px 0; border-top: 1px solid currentcolor; font-size: 14px; opacity: .9; }
.v2 .v2_stack .body li span { color: var(--v2-accent); }

.v2 .v2_svc_cta { padding: 96px var(--v2-pad); background: var(--v2-ink); color: var(--v2-paper); }
.v2 .v2_svc_cta h2 { max-width: 896px; font-size: 30px; line-height: 1.4; word-break: keep-all; }
.v2 .v2_svc_cta .v2_pill { margin-top: 40px; }
.v2 .v2_pill.is_paper { background: var(--v2-paper); color: var(--v2-ink); }
.v2 .v2_pill.is_paper:hover { background: var(--v2-accent); color: var(--v2-on-accent); }

/* --- Pinned statement --------------------------------------------------- */
/* 300vh of runway with a 100svh block pinned inside it: the block holds for one
   runway minus one pinned block, and that distance is what every mapping in
   v2.js is measured against. Change the runway and the timing changes with it. */
.v2 .v2_shout { position: relative; height: 300vh; background: var(--v2-paper); }
.v2 .v2_shout .pin { position: sticky; top: 0; display: flex; flex-direction: column; justify-content: space-between; overflow: hidden; height: 100svh; padding: 40px var(--v2-pad); color: var(--v2-ink); }
.v2 .v2_shout .head { opacity: .5; }
.v2 .v2_shout .stage { position: relative; flex-grow: 1; }
/* Both lines are centred on the same spot and cross-fade in place, so they have
   to come out of flow - stacked normally they would push each other around. */
.v2 .v2_shout .quiet,
.v2 .v2_shout .shout { position: absolute; left: 0; top: 50%; transform: translateY(-50%); }
.v2 .v2_shout .quiet { max-width: 768px; font-size: 36px; line-height: 1.375; }
.v2 .v2_shout .shout { opacity: 0; font-size: 9vw; line-height: .85; }
.v2 .v2_shout .loud { color: var(--v2-accent); }
.v2 .v2_shout .db { margin-bottom: 16px; text-align: right; opacity: .5; }
.v2 .v2_shout .bars { display: flex; align-items: flex-end; gap: 8px; height: 30vh; }
/* The 28 bars are built and lit by v2.js, which is why they may be empty
   elements: script-driven, not decorative markup. .12 is a bar's resting
   opacity before the script paints it. */
.v2 .v2_shout .bars span { flex: 1 1 0; background: currentColor; opacity: .12; }
.v2 .v2_shout .bars span.is_hot { background: var(--v2-accent); }

/* --- Contact CTA -------------------------------------------------------- */
.v2 .v2_cta { padding: 144px var(--v2-pad); background: var(--v2-accent); color: var(--v2-on-accent); }
.v2 .v2_cta .head { color: rgba(10, 10, 10, .6); }
.v2 .v2_cta .title { max-width: 1024px; margin-top: 16px; font-size: 96px; line-height: 1; }
.v2 .v2_cta .actions { display: flex; flex-wrap: wrap; gap: 16px; margin-top: 48px; }
.v2 .v2_cta .v2_pill { height: 60px; padding-inline: 40px; transition: transform .3s var(--v2-ease), background-color .3s var(--v2-ease); }
.v2 .v2_cta .v2_pill:hover { background: var(--v2-ink); transform: scale(1.05); }
/* site.css owns a .note and sets it in the mono register. This one is prose,
   and no element reset reaches a class collision - it has to be said here. */
.v2 .v2_cta .note { margin-top: 24px; font-family: inherit; letter-spacing: normal; font-size: 14px; line-height: 1.7; color: rgba(10, 10, 10, .7); }

@media (max-width: 767px) {
    .v2 { --v2-pad: 20px; }
    .v2_header { padding-inline: 20px; }
    .v2_header .v2_gnb { display: none; }
    .v2_burger { display: block; }
    .v2_menu { padding-inline: 20px; }
    .v2_menu li a { font-size: 34px; }
    .v2 .v2_hero .foot { flex-direction: column; align-items: stretch; gap: 32px; margin-top: 40px; }
    .v2 .v2_hero .cue { display: none; }
    .v2 .v2_pagehead { padding: 144px var(--v2-pad) 64px; }
    /* The author column is the first thing to go: it is masked anyway, and the
       title needs the width more than a row of 이*빈 does. */
    .v2 .v2_board .who { display: none; }
    .v2 .v2_board td { padding: 16px 8px; }
    .v2 .v2_board th { padding: 12px 8px; }
    .v2 .v2_board td.subject a { font-size: 16px; }
    .v2 .v2_board .detail dt { flex: 1 1 100%; }
    .v2 .post_head { margin-bottom: 32px; }
    .v2 .post_head .post_title { font-size: 20px; }
    /* 13.5vw, not the study's 15: PORTFOLIO is the longest line any sub-page
       takes and it fits up to 14.00vw at 360, where the 20px gutters cost the
       most. A line that wraps rises as a block and the clip effect is lost. */
    .v2 .v2_pagehead .title { font-size: 13.5vw; }
    .v2 .v2_services .body { padding-block: 96px; }
    .v2 .v2_services .title { font-size: 48px; }
    .v2 .v2_services .name { font-size: 30px; line-height: 1.2; overflow-wrap: anywhere; }
    .v2 .v2_services .names { flex-direction: column; gap: 4px; }
    .v2 .v2_services .list a { min-width: 0; }
    .v2 .v2_services .label { flex-wrap: wrap; min-width: 0; }
    .v2 .v2_grid { padding-block: 72px; }
    .v2 .v2_grid .head h2 { font-size: 32px; }
    .v2 .v2_nos .row > span { margin-inline: 20px; font-size: 15px; }
    .v2 .v2_band { padding-bottom: 72px; }
    .v2 .v2_icon { margin-bottom: 12px; }
    .v2 .v2_stack { padding-bottom: 96px; }
    .v2 .v2_stack h2 { font-size: 32px; }
    .v2 .v2_form { padding-bottom: 96px; }
    .v2 .v2_about_hero { padding-top: 128px; }
    .v2 .v2_svc_hero { padding-top: 128px; }
    .v2 .v2_contact_hero { padding-top: 128px; }
    .v2 .v2_stack h2 { font-size: 32px; }
    .v2 .v2_svc_cta h2 { font-size: 24px; }
    .v2 .v2_team { padding-block: 72px; }
    .v2 .v2_team .head h2 { font-size: 32px; }
    .v2 .v2_team .list > li { flex-basis: 100%; }
    .v2 .v2_nos .track > span { margin-inline: 20px; font-size: 16px; }
    .v2 .v2_manifesto { padding-block: 96px; }
    .v2 .v2_manifesto .body { font-size: 22px; }
    .v2 .v2_rules { padding-block: 72px; }
    .v2 .v2_rules .head { font-size: 32px; }
    .v2 .v2_portfolio { padding-block: 72px; }
    .v2 .v2_portfolio .head h2 { font-size: 48px; }
    .v2 .v2_portfolio .list > li { flex-basis: 100%; }
    .v2 .v2_shout .pin { padding-block: 40px; }
    .v2 .v2_shout .quiet { font-size: 24px; }
    .v2 .v2_shout .shout { font-size: 12vw; }
    .v2 .v2_shout .bars { height: 24vh; gap: 4px; }
    .v2 .v2_cta { padding-block: 96px; }
    /* 40px, not 48: STOREFRONT. is the longest word any CTA takes and it fits
       up to 41.9px at 360, where the gutters cost the most. At 48 the
       overflow-wrap below was doing the work and breaking it as STOREFRON / T.
       The rule stays as a backstop for a word nobody has written yet. */
    .v2 .v2_cta .title { font-size: 40px; overflow-wrap: anywhere; }
}

@media (min-width: 768px) {
    .v2 .v2_manifesto { padding-block: 176px; }
    .v2 .v2_about_hero { padding-top: 176px; }
    .v2 .v2_svc_hero { padding-top: 176px; }
    .v2 .v2_contact_hero { padding-top: 176px; }
    .v2 .v2_contact_hero .title { font-size: 13vw; }
    .v2 .v2_svc_hero .title { font-size: 10.5vw; }
    .v2 .v2_stack .inner { padding: 56px 48px; }
    .v2 .v2_stack h2 { font-size: 72px; }
    .v2 .v2_stack .body { flex-direction: row; gap: 64px; }
    .v2 .v2_stack .body p { flex: 1 1 55%; font-size: 18px; }
    .v2 .v2_stack .body ul { flex: 1 1 45%; align-self: end; }
    .v2 .v2_svc_cta { padding-block: 128px; }
    .v2 .v2_svc_cta h2 { font-size: 44px; }
    /* 9.8vw, not the study's 10.5: TESTED TO THE wraps at 768 above that. */
    .v2 .v2_about_hero .title { font-size: 9.8vw; }
    .v2 .v2_manifesto .body { font-size: 42px; }
    .v2 .v2_team { padding-block: 128px; }
    .v2 .v2_team .head h2 { font-size: 72px; }
    .v2 .v2_team .list > li { flex-basis: calc(33.3333% - 16px); }
    .v2 .v2_manifesto .cols { flex-wrap: nowrap; gap: 80px; }
    .v2 .v2_manifesto .text { flex-basis: 58%; }
    .v2 .v2_manifesto .shot { flex-basis: 42%; aspect-ratio: 3 / 4; }
    .v2_strip { height: 320px; }
    .v2_strip.is_tall { height: 440px; }
    .v2 .v2_grid { padding-block: 128px; }
    .v2 .v2_grid .head h2 { font-size: 72px; }
    .v2 .v2_grid > .list > li { flex-basis: calc(50% - 12px); }
    .v2 .v2_services .list.is_static .detail { padding-left: 48px; }
    .v2 .v2_stack .inner { padding: 56px 48px; }
    .v2 .v2_stack h2 { font-size: 72px; }
    .v2 .v2_stack .body { flex-wrap: nowrap; gap: 64px; }
    .v2 .v2_stack .text { flex-basis: 55%; }
    .v2 .v2_stack .facts { flex-basis: 45%; }
    .v2 .v2_stack .facts li { flex-direction: row; align-items: baseline; justify-content: space-between; gap: 24px; }
    .v2 .v2_stack .facts .detail { flex: 0 1 55%; text-align: right; }
    .v2 .v2_form .cols { flex-wrap: nowrap; gap: 96px; }
    .v2 .v2_form .brief { flex-basis: 60%; }
    .v2 .v2_form .aside { flex-basis: 40%; }
    .v2 .v2_manifesto .body { font-size: 42px; }
    .v2 .v2_rules { padding-block: 128px; }
    .v2 .v2_rules .head { font-size: 72px; }
    .v2 .v2_rules .rule { flex-basis: calc(50% - 24px); }
    .v2 .v2_portfolio .head h2 { font-size: 96px; }
    .v2 .v2_portfolio .list > li { flex-basis: calc(33.3333% - 16px); }
}

/* Authoritative: the site's own reduced-motion block in site.css already resets
   transforms with !important, but the hero line animation and the marquee are
   declared here and need naming. */
@media (prefers-reduced-motion: reduce) {
    .v2 .v2_hero .line > span { transform: none; animation: none; }
    .v2 .v2_pagehead .line > span { transform: none; animation: none; }
    .v2 .v2_hero .cue::before { animation: none; clip-path: inset(0 0 66.7% 0); }
    .v2 .v2_marquee .track { animation: none; }
    .v2 .v2_nos .track { animation: none; }
    .v2 .v2_about_hero .line > span { transform: none; animation: none; }
    .v2 .v2_svc_hero .line > span { transform: none; animation: none; }
    .v2 .v2_contact_hero .line > span { transform: none; animation: none; }
    .v2 .v2_manifesto .body span { opacity: 1; }
    .v2 .v2_icon [class^="v2_"] { animation: none; opacity: 1; transform: none; stroke-dashoffset: 0; }
    .v2_curtain { animation: none; transform: translateY(-100%); }
    .v2-motion [data-reveal] { opacity: 1; transform: none; transition: none; }
}

/* --- Footer ------------------------------------------------------------- */
.v2_footer { background: var(--v2-ink); color: var(--v2-paper); }
.v2_footer .top { padding: 80px 40px 0; }
.v2_footer .cols { display: flex; gap: 48px; padding-bottom: 80px; }
.v2_footer .col { flex: 1 1 0; min-width: 0; }
.v2_footer .label { color: var(--v2-paper-50); }
.v2_footer .start { display: inline-flex; align-items: baseline; gap: 12px; margin-top: 12px; font-size: 24px; line-height: 1.3333; color: inherit; text-decoration: none; transition: color .3s var(--v2-ease); }
.v2_footer .start:hover { color: var(--v2-accent); }
.v2_footer .start .arrow { color: var(--v2-accent); transition: transform .3s var(--v2-ease); }
.v2_footer .start:hover .arrow { transform: translateX(8px); }
.v2_footer address { margin-top: 12px; font-size: 20px; line-height: 1.625; font-style: normal; }
.v2_footer .col ul { margin-top: 12px; font-size: 20px; line-height: 1.4; list-style: none; }
.v2_footer .col ul a { color: inherit; text-decoration: none; }
/* The legal row. 12px is where the study puts its own fine print, and it is the
   right weight for 전자상거래법 제10조 disclosure: present and checkable, not
   competing with the work. */
.v2_footer .meta { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 16px; padding-block: 16px; border-top: 1px solid var(--v2-paper-20); font-size: 12px; line-height: 1.5; color: var(--v2-paper-50); }
.v2_footer .meta a { color: rgba(250, 250, 250, .7); text-decoration: none; margin-left: 12px; }
.v2_footer .meta a:first-child { margin-left: 0; }
.v2_footer .meta a:hover { color: var(--v2-accent); }
.v2_footer .wordmark { overflow: hidden; padding-bottom: 8px; font-size: 18.5vw; line-height: .8; text-align: center; white-space: nowrap; transition: color .5s var(--v2-ease); }
.v2_footer .wordmark:hover { color: var(--v2-accent); }

@media (max-width: 767px) {
    .v2_footer .top { padding: 64px 20px 0; }
    .v2_footer .cols { flex-wrap: wrap; gap: 48px 24px; padding-bottom: 56px; }
    .v2_footer .col { flex: 1 1 40%; }
    .v2_footer .col.is_wide { flex-basis: 100%; padding-bottom: 40px; border-bottom: 1px solid var(--v2-paper-20); }
    .v2_footer address,
    .v2_footer .col ul { font-size: 16px; }
}

/* --- Pages not yet in this register -------------------------------------- */
/* The v2 header is fixed, so a page that does not build its own top spacing
   starts underneath it. The old register's own pages (.home) already reserve
   the band because their bar was absolute; the plain document pages never had
   to, since the bar used to be sticky and pushed them down. */
main:not(:has(.v2)):not(:has(.home)) { padding-top: 96px; }
