/* ============================================================
   Erica Rueda — portfolio
   One stylesheet for every page: the identity cannot drift if
   there is only one place it is defined. See DESIGN.md.
   ============================================================ */
/* ============ TOKENS ============ */
:root{
  --violet:#2B1A6E;          /* core brand violet */
  --violet-deep:#1B0F4D;
  --violet-ink:#14102E;
  --lime:#DDF56A;
  --black:#0A0A0A;
  --paper:#E9E7EE;
  --white:#FFFFFF;
  --pink:#E72787;            /* NV accent, loaded-brush cursor color */
  --silver:#C0C2C7;          /* cover badge grey, sampled from Portfolio 2026.pdf */
  /* The case-study palettes now live inside the rendered boards; this one
     stays because it tints the cover's atmosphere. */
  --nv-3:#44A4DC;
  /* case-study values sampled straight from Portfolio 2026.pdf */
  --pdf-plaque:#1A0051;      /* section title plaque */
  --pdf-board:#CCCCD2;       /* About Me board */
  --pdf-contact:#CDCDD2;     /* contact strip under the portrait */
  /* The cover's ground. Erica's value, and the same deep violet the cover
     fluid carries in its own palette — so the effect sits on its own colour
     rather than on top of a second one. */
  --hero-ink:#17014e;
  --pdf-violet:#1A0051;      /* plaque / tile / ink violet */
  /* ---- type: one voice ----
     The whole site is set in Sora and nothing else (Erica's call, 2026-08-21).
     One family across Home, About, Work and Contact is what makes the four
     pages read as one site, so there is exactly one family token — never add
     a second face here.

     Because the family no longer varies, WEIGHT carries the role. Sora is a
     variable face spanning 100–800: there is no italic and no true 900, so
     the ramp tops out at ExtraBold and nothing in this sheet may ask for
     `font-style:italic` (the browser would synthesise a fake oblique). */
  --font:'Sora', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --w-display:800;   /* cover wordmark, plaques, board headings */
  --w-strong:700;    /* nav brand, indices, buttons, tags, chrome */
  --w-chrome:600;    /* smaller chrome: badge, marquee, labels */
  --w-data:500;      /* contact details */
  --w-body:400;      /* reading copy */
  --pad:clamp(1.25rem, 4vw, 4rem);
  --radius:20px;
  /* ---- entrance timing ----
     Every entrance on the page reads from these three, so the whole pace of
     the site is tunable here. They govern arrivals only: hover and menu
     feedback stay immediate, because a delayed hover reads as a broken page,
     not as a slow one. */
  --anim-delay:1s;    /* the beat before an entrance starts */
  --anim-dur:2s;      /* how long it then takes */
  --anim-step:250ms;  /* between siblings, so they arrive one at a time */
  --focus-dur:2.8s;   /* the case-study focus pull — slower still, one piece */
}
/* ---- the quick pace (Work, Erica's call 2026-08-21) ----
   Work is the page a reviewer opens to see the work, and at the site's normal
   1s + 2s beat it reads as an empty page that has stopped loading rather than
   a slow one. Everything on it arrives inside a second. The tokens are still
   the only dial — this overrides the three values, it does not reach into a
   single component. Home, About and Contact keep the slow pace, because there
   the wait is over a cover or a personal page, not over the portfolio itself. */
body.pace-quick{
  --anim-delay:.04s;
  --anim-dur:.85s;
  --anim-step:90ms;
}
/* Sora is loaded from Google Fonts by each page's <head>; there are no local
   @font-face rules any more. The two faces that used to live here — StretchPro
   and Coolvetica — were rebuilt from the subsets embedded in Portfolio 2026.pdf
   and their licences were never confirmed, which was this project's one open
   risk before publishing. Moving to Sora (SIL Open Font License) retires that
   risk, and with it every workaround the subsets forced: the missing glyphs
   (J K Q W X Z w x 1 4 8 9), the unicode-range that carved out the apostrophe
   because the subset drew it as a grave accent, and the separate face for the
   ampersand. The old woff2 files are parked in assets/_unused/fonts-pdf/. */
*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth;scroll-padding-top:5.5rem}
/* Reveals park artwork just outside the viewport, so the page must never grow
   a horizontal scrollbar. `clip` (unlike `hidden`) does not create a scroll
   container, so it cannot break position:sticky or anchor scrolling. */
html,body{overflow-x:hidden;overflow-x:clip}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.001s !important;transition-duration:.001s !important}
  /* A looping animation needs stopping, not shortening: collapsing an infinite
     loop to .001s spins it a thousand times a second instead of stilling it.
     Every new infinite animation MUST be added here — see The Still-The-Loop
     Rule in DESIGN.md. */
  .marquee .track,
  .sc,                 /* the drifting collage marks */
  .sticker.alive       /* the two stickers that never settle */
  {animation:none !important}
}
body{
  font-family:var(--font);font-weight:var(--w-body);
  background:var(--violet);
  color:var(--white);
  -webkit-font-smoothing:antialiased;
}
::selection{background:var(--lime);color:var(--violet)}
img{display:block;max-width:100%;height:auto}
a{color:inherit}
/* Form controls do not inherit type from their ancestors — the user agent
   hands them its own font, which is how every gallery caption, carousel
   arrow and video control on this site was quietly setting itself in Arial.
   Only the family is inherited here: the rules that style these controls
   already declare their own size and weight, so nothing reflows. */
button,input,select,textarea{font-family:inherit}
/* The skills tubes are built from <i> (the glass) and <b> (the liquid),
   chosen for brevity, not meaning — they hold no text, so the italic and
   bold the user agent gives them are undone rather than inherited. */
.ab-lvl i,.ab-lvl b{font-style:normal;font-weight:inherit}
:focus-visible{outline:3px solid var(--lime);outline-offset:3px;border-radius:4px}
.section-about :focus-visible,.section-bvi :focus-visible{outline-color:var(--violet)}

/* The click spark overlay. Created by the script, never in the markup: it
   carries no content and is inert to the pointer, so there is nothing for the
   HTML to say about it. Fixed to the viewport so a burst lands where the
   click did, whatever the page has scrolled to. */
.click-spark{
  position:fixed;inset:0;width:100%;height:100%;
  pointer-events:none;z-index:120;
}

/* ============ BRUSH CURSOR ============
   A real brush, drawn back to front: thin lime handle, a silver ferrule
   band, and a splayed bristle bundle wider than the shaft — the ferrule is
   what stops it reading as a pencil, so never drop it. Lime bristles at
   rest, pink-loaded bristles plus a paint dab over anything interactive.
   Black keyline keeps it readable on violet, near-black, paper and cream.
   Fine pointers only; falls back to auto/pointer where SVG cursors fail.
   On About the brush also paints — see THE PAINT TRAIL below. */
@media (hover:hover) and (pointer:fine){
  body{
    cursor:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cline x1='15' y1='15' x2='27.5' y2='27.5' stroke='%230A0A0A' stroke-width='7.6' stroke-linecap='round'/%3E%3Cline x1='15.6' y1='15.6' x2='26.8' y2='26.8' stroke='%23DDF56A' stroke-width='3.8' stroke-linecap='round'/%3E%3Cline x1='11' y1='11' x2='15.4' y2='15.4' stroke='%230A0A0A' stroke-width='9.4'/%3E%3Cline x1='11.4' y1='11.4' x2='15' y2='15' stroke='%23C0C2C7' stroke-width='6.4'/%3E%3Cpath d='M2.2 2.2 C 6.8 5.2 11.4 7.4 15.2 10.2 L 10.2 15.2 C 7.4 11.4 5.2 6.8 2.2 2.2 Z' fill='%23DDF56A' stroke='%230A0A0A' stroke-width='1.7' stroke-linejoin='round'/%3E%3Cpath d='M4.6 4.6 L 12.4 10.4 M6.6 3.4 L 13.2 8.2' stroke='%230A0A0A' stroke-width='.9' opacity='.5'/%3E%3C/svg%3E") 2 2, auto;
  }
  a,button,summary,[role="button"],.art{
    cursor:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cline x1='15' y1='15' x2='27.5' y2='27.5' stroke='%230A0A0A' stroke-width='7.6' stroke-linecap='round'/%3E%3Cline x1='15.6' y1='15.6' x2='26.8' y2='26.8' stroke='%23DDF56A' stroke-width='3.8' stroke-linecap='round'/%3E%3Cline x1='11' y1='11' x2='15.4' y2='15.4' stroke='%230A0A0A' stroke-width='9.4'/%3E%3Cline x1='11.4' y1='11.4' x2='15' y2='15' stroke='%23C0C2C7' stroke-width='6.4'/%3E%3Cpath d='M2.2 2.2 C 6.8 5.2 11.4 7.4 15.2 10.2 L 10.2 15.2 C 7.4 11.4 5.2 6.8 2.2 2.2 Z' fill='%23E72787' stroke='%230A0A0A' stroke-width='1.7' stroke-linejoin='round'/%3E%3Cpath d='M4.6 4.6 L 12.4 10.4 M6.6 3.4 L 13.2 8.2' stroke='%230A0A0A' stroke-width='.9' opacity='.45'/%3E%3C/svg%3E") 2 2, pointer;
  }
}

/* ============ PROGRESS + NAV ============ */
#progress{position:fixed;top:0;left:0;height:3px;width:0;background:var(--lime);z-index:120}
nav.site-nav{
  position:fixed;top:0;left:0;right:0;z-index:110;
  display:flex;align-items:center;justify-content:space-between;
  padding:.9rem var(--pad);
  background:color-mix(in srgb, var(--violet-deep) 82%, transparent);
  backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);
  border-bottom:1px solid color-mix(in srgb, var(--lime) 18%, transparent);
}
nav.site-nav .brand{font-family:var(--font);font-weight:var(--w-display);font-size:.95rem;text-decoration:none;letter-spacing:.02em}
nav.site-nav ul{display:flex;gap:clamp(.75rem,2.5vw,2rem);list-style:none;align-items:center}
nav.site-nav ul a{
  text-decoration:none;font-size:.85rem;font-weight:500;letter-spacing:.08em;text-transform:uppercase;
  opacity:.85;transition:opacity .2s;
}
nav.site-nav ul a:hover{opacity:1;color:var(--lime)}
nav.site-nav ul a.cta{
  background:var(--lime);color:var(--violet);opacity:1;font-weight:700;
  padding:.5rem 1.1rem;border-radius:999px;transition:transform .2s;
}
nav.site-nav ul a.cta:hover{transform:translateY(-2px);color:var(--violet)}
/* ---- Work menu ----
   The cover no longer lists the bodies of work; this is the one place they
   are enumerated, so a new section only ever means one more <li> below. */
.nav-menu{position:relative}
.nav-toggle{
  font:inherit;font-size:.85rem;font-weight:500;letter-spacing:.08em;
  text-transform:uppercase;color:inherit;background:none;border:0;padding:0;
  display:flex;align-items:center;gap:.5em;opacity:.85;transition:opacity .2s;
}
.nav-toggle:hover,.nav-toggle[aria-expanded="true"]{opacity:1;color:var(--lime)}
.nav-toggle .chev{
  width:.42em;height:.42em;border-right:2px solid currentColor;border-bottom:2px solid currentColor;
  transform:translateY(-.15em) rotate(45deg);transition:transform .22s ease;
}
.nav-toggle[aria-expanded="true"] .chev{transform:translateY(.05em) rotate(-135deg)}
/* outranks the `nav.site-nav ul` flex row above, which would otherwise keep
   its own gap and centring on this nested list */
nav.site-nav .nav-drop{
  position:absolute;top:calc(100% + 1.1rem);left:50%;translate:-50% 0;
  flex-direction:column;align-items:stretch;gap:.15rem;min-width:13.5rem;
  background:var(--violet-deep);border:1px solid color-mix(in srgb,var(--lime) 30%, transparent);
  border-radius:16px;padding:.45rem;box-shadow:6px 6px 0 rgba(0,0,0,.4);
}
nav.site-nav .nav-drop[hidden]{display:none}
nav.site-nav .nav-drop a{
  display:block;padding:.62rem .95rem;border-radius:11px;white-space:nowrap;letter-spacing:.06em;
  transition:background .18s,color .18s,opacity .18s;
}
nav.site-nav .nav-drop a:hover,nav.site-nav .nav-drop a:focus-visible{
  background:color-mix(in srgb,var(--lime) 14%, transparent);color:var(--lime);opacity:1;
}

/* ============ LABELS ============ */
.kicker{font-size:.8rem;letter-spacing:.22em;text-transform:uppercase;opacity:.6;margin-bottom:.75rem}
/* visually hidden, still read aloud — keeps the heading outline intact where
   the design carries the name as a logotype image */
.vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}

/* ============ SELECTION FRAME (shared: hero + section plaque) ============ */
.sel-frame{
  --h:clamp(11px,1.5vw,24px);
  position:relative;border:1.5px solid var(--lime);
}
.sel-frame .handle{
  position:absolute;width:var(--h);height:var(--h);
  background:var(--violet);border:1.5px solid var(--lime);
}
.h-tl{top:calc(var(--h)/-2);left:calc(var(--h)/-2)}
.h-tc{top:calc(var(--h)/-2);left:calc(50% - var(--h)/2)}
.h-tr{top:calc(var(--h)/-2);right:calc(var(--h)/-2)}
.h-ml{top:calc(50% - var(--h)/2);left:calc(var(--h)/-2)}
.h-mr{top:calc(50% - var(--h)/2);right:calc(var(--h)/-2)}
.h-bl{bottom:calc(var(--h)/-2);left:calc(var(--h)/-2)}
.h-bc{bottom:calc(var(--h)/-2);left:calc(50% - var(--h)/2)}
.h-br{bottom:calc(var(--h)/-2);right:calc(var(--h)/-2)}

/* ============ SECTION PLAQUE ============
   One header for every section, as in Portfolio 2026.pdf: a violet plaque
   inside a selection frame. Index and title are one family now, separated
   by weight and colour alone. */
.plaque{
  width:max-content;max-width:min(92%,640px);
  margin:0 auto clamp(2.5rem,6vw,4.5rem);
  padding:clamp(.4rem,1vw,.75rem);
}
.plaque .box{
  background:var(--pdf-plaque);border-radius:clamp(12px,1.6vw,26px);
  padding:clamp(1rem,2.6vw,2rem) clamp(1.5rem,3.6vw,3rem);
  display:flex;align-items:center;gap:clamp(.75rem,2vw,1.4rem);
}
.plaque .idx{
  font-family:var(--font);font-weight:var(--w-strong);
  font-size:clamp(.8rem,1.9vw,1.3rem);line-height:1;letter-spacing:.02em;
  color:var(--lime);opacity:.5;
  align-self:stretch;display:flex;align-items:center;
  padding-right:clamp(.75rem,2vw,1.4rem);
  border-right:1px solid color-mix(in srgb,var(--lime) 32%, transparent);
}
.plaque :is(h1,h2){
  font-family:var(--font);font-weight:var(--w-display);color:var(--lime);
  font-size:clamp(1.3rem,3.7vw,2.6rem);line-height:1.18;letter-spacing:.01em;
  text-transform:uppercase;text-align:center;
}
/* One family now carries the ampersand too; it only stays slightly smaller
   so the glyph's larger drawn size sits level with the caps beside it. */
.plaque .amp{font-size:.86em}

/* ============ HERO ============ */
header.hero{
  /* The badge hangs above the frame, outside the flow, so the cover has to
     reserve room for it by hand: nav height plus the badge's own height
     (~4.3em of its font-size). Without the reserve "Graphic Design" slides
     under the nav bar on short viewports. */
  --badge-fs:clamp(.75rem,1.7vw,1.5rem);
  min-height:100svh;display:flex;flex-direction:column;justify-content:center;
  position:relative;padding:calc(4.6rem + 4.3 * var(--badge-fs)) var(--pad) 0;
  /* One flat colour and nothing else (Erica's call, 2026-08-21). The mosaic
     of her work that used to be pinned here is gone — the cover is now the
     fluid on its own ground, and an image behind it only competed. This is
     also what the type's contrast is measured against, so changing it means
     re-measuring (see DESIGN.md). */
  background:var(--hero-ink);
  overflow:hidden;
}
/* ---- the cover's ground ----
   Nothing but the flat colour on header.hero and the fluid over it. There is
   no image layer and no scrim any more: with one colour underneath, the
   scrim had nothing left to protect the type from.
   `screen` tints that violet rather than covering it, so the fluid glows
   instead of sitting on the page as a flat sheet, and the canvas stays fully
   transparent until the simulation puts something there.
   The mask is the contrast guard: the fluid is free in the upper two thirds
   and gone by the bottom, where the name, year and subtitle live. Without it
   a bright lime vortex can drift under lime type and take the cover below AA
   — measured, not assumed. */
.hero-bg{position:absolute;inset:0;z-index:0;overflow:hidden}
.hero-fx{
  position:absolute;inset:0;width:100%;height:100%;display:block;
  mix-blend-mode:screen;opacity:1;
  -webkit-mask-image:linear-gradient(180deg,#000 0%,#000 58%,transparent 88%);
  mask-image:linear-gradient(180deg,#000 0%,#000 58%,transparent 88%);
}
/* The cover's own content rides above the ground. Named rather than written
   as :not(.hero-bg): that form also caught the pause button and overrode the
   `position:absolute` it needs, dropping it into the flow at the top left. */
header.hero > .hero-frame,
header.hero > .hero-meta,
header.hero > .hero-sub{position:relative;z-index:1}
.hero-vidbtn{right:1.15rem;bottom:1.15rem}
header.hero::after{ /* film grain */
  content:"";position:absolute;inset:0;pointer-events:none;opacity:.05;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}
.hero-frame{
  --fpad:clamp(1rem,4vw,4rem);
  margin-inline:auto;width:min(1240px,100%);
  padding:clamp(1.5rem,4.5vw,3.5rem) var(--fpad);
}
/* two-line "cloud" badge overlapping the frame's top-right corner */
.hero-badge{
  position:absolute;right:-1.2em;bottom:calc(100% - .55em);
  font-size:var(--badge-fs);text-align:center;
}
.hero-badge i{
  display:inline;font-style:normal;
  -webkit-box-decoration-break:clone;box-decoration-break:clone;
  font-family:var(--font);font-weight:var(--w-chrome);line-height:1.6;
  background:var(--silver);color:var(--black);
  padding:.42em .8em;border-radius:999px;
}
.hero-box{
  background:var(--black);border-radius:clamp(18px,3vw,34px);
  padding:clamp(1.6rem,5vw,3.6rem) clamp(1rem,4vw,3rem);
  text-align:center;
}
.hero-box h1{
  font-family:var(--font);font-weight:var(--w-display);color:var(--lime);
  font-size:clamp(2.3rem,8.4vw,6.6rem);letter-spacing:-.005em;line-height:1;
  text-transform:uppercase;white-space:nowrap;
}
/* meta + sub align to the black box edges, not the frame edges */
.hero-meta{
  display:flex;justify-content:space-between;align-items:baseline;
  width:min(1240px,100%);margin:1.5rem auto 0;gap:1rem;flex-wrap:wrap;
  padding-inline:clamp(1rem,4vw,4rem);
}
.hero-meta .name,.hero-meta .year{
  font-family:var(--font);font-weight:var(--w-strong);color:var(--lime);
  font-size:clamp(1rem,2.6vw,1.7rem);
}
.hero-sub{
  width:min(1240px,100%);margin:.9rem auto 0;
  padding-inline:clamp(1rem,4vw,4rem);
  font-size:clamp(.9rem,1.6vw,1.05rem);opacity:.7;
}
.hero-sub span{display:block;max-width:52ch}

/* ============ MARQUEE ============ */
.marquee{
  background:var(--lime);color:var(--violet);overflow:hidden;white-space:nowrap;
  padding:.8rem 0;border-block:2px solid var(--black);
}
.marquee .track{display:inline-block;animation:scroll 45s linear infinite}
.marquee span{
  font-family:var(--font);font-weight:var(--w-chrome);font-size:.9rem;
  letter-spacing:.12em;text-transform:uppercase;padding-inline:1.2rem;
}
@keyframes scroll{from{transform:translateX(0)}to{transform:translateX(-50%)}}

/* ============ GENERIC SECTIONS ============ */
section{padding:clamp(4rem,9vw,7.5rem) var(--pad)}
.wrap{max-width:1250px;margin-inline:auto}
.section-dark{background:var(--black)}

/* ============ REVEAL ENGINE ============
   A piece slides in from outside the viewport, scaling up, and settles with
   a bounce. JS parks each element (--rx/--ry) on the nearest screen edge and
   removes the parking the first time it comes into view.
   Entrances LATCH: `.in` is added once and never taken off again, so
   scrolling back up leaves everything exactly where it settled. See The
   Arrive-Once Rule in DESIGN.md. Classes are added by JS only, so a no-JS
   visitor sees everything immediately instead of a blank page. */
.rv{
  opacity:0;
  transform:translate3d(var(--rx,0px),var(--ry,0px),0) scale(var(--rs,.7));
  transition:
    opacity calc(var(--anim-dur) * .6) ease-out,
    transform var(--rdur,var(--anim-dur)) cubic-bezier(.18,1.3,.36,1);
  /* JS supplies only the sibling offset in --rdel; the base beat is shared */
  transition-delay:calc(var(--anim-delay) + var(--rdel,0ms));
  will-change:transform,opacity;
}
.rv.in{opacity:1;transform:translate3d(0,0,0) scale(1)}
/* text rides in on the same motion, but shallower so it stays readable */
.rv-text{--rs:.93}
/* measuring pass: read untransformed geometry */
.rv-measuring .rv{transform:none !important;transition:none !important}

/* ============ TYPEWRITER (About Me) ============ */
.tw-c{opacity:0}
.tw-c.on{opacity:1}
.tw-c.cur{position:relative}
.tw-c.cur::after{
  content:"";position:absolute;left:100%;top:.06em;bottom:.04em;width:2px;
  background:currentColor;animation:tw-blink .75s steps(1,end) infinite;
}
@keyframes tw-blink{0%,50%{opacity:1}50.01%,100%{opacity:0}}

/* ============ ART GRIDS ============ */
.campaign{margin-bottom:clamp(3rem,7vw,5rem)}
.campaign:last-child{margin-bottom:0}
.camp-head{display:flex;align-items:baseline;gap:1rem;flex-wrap:wrap;margin-bottom:1.4rem}
.camp-head h3{font-family:var(--font);font-weight:var(--w-display);font-size:clamp(1.05rem,2.4vw,1.5rem)}
.tags{display:flex;gap:.5rem;flex-wrap:wrap}
.tag{
  font-size:.68rem;letter-spacing:.1em;text-transform:uppercase;font-weight:500;
  border:1px solid color-mix(in srgb,currentColor 45%, transparent);
  padding:.3em .9em;border-radius:999px;opacity:.8;
}
.grid{display:grid;gap:clamp(.8rem,2vw,1.4rem)}
.grid-3{grid-template-columns:repeat(3,1fr)}
.grid-posters{grid-template-columns:repeat(3,1fr)}
.art{
  position:relative;border-radius:var(--radius);overflow:hidden;
  border:0;background:none;padding:0;display:block;width:100%;
}
.art img{width:100%;height:100%;object-fit:cover;transition:transform .5s ease}
.art:hover img{transform:scale(1.03)}
.art .sel{ /* selection frame overlay on hover */
  position:absolute;inset:8px;border:1.5px solid var(--lime);opacity:0;
  transition:opacity .25s;pointer-events:none;
  background-image:
    linear-gradient(var(--lime),var(--lime)),linear-gradient(var(--lime),var(--lime)),
    linear-gradient(var(--lime),var(--lime)),linear-gradient(var(--lime),var(--lime));
  background-size:9px 9px;background-repeat:no-repeat;
  background-position:top left,top right,bottom left,bottom right;
}
.art:hover .sel,.art:focus-visible .sel{opacity:1}
.art figcaption,.art .cap{
  position:absolute;left:14px;bottom:14px;
  background:var(--black);color:var(--lime);
  font-size:.72rem;font-weight:500;letter-spacing:.06em;
  padding:.4em 1em;border-radius:999px;
  opacity:0;transform:translateY(6px);transition:.28s;pointer-events:none;
}
.art:hover .cap,.art:focus-visible .cap{opacity:1;transform:none}

/* ============================================================
   ABOUT ME — rebuilt from Portfolio 2026.pdf
   The board is the PDF's own 1920 × 1081 artboard. Every position,
   measure and type size below is a percentage of it (via container
   query units), so the reference grid holds at any width.
   ============================================================ */
.section-about{padding:0;background:var(--pdf-board);color:var(--pdf-violet)}
.ab{
  position:relative;max-width:1920px;margin-inline:auto;
  container-type:inline-size;
  aspect-ratio:1920/1081;
}
/* max() keeps the copy legible once the board is narrower than ~1300px,
   where a straight proportional size would drop under 12px */
.ab-h{font-family:var(--font);font-weight:var(--w-display);font-size:max(1.05rem,1.484cqw);line-height:1.44}
.ab-p{font-family:var(--font);font-weight:var(--w-body);font-size:max(.72rem,.948cqw);line-height:1.264}
.ab-h + .ab-p{margin-top:.45em}
.ab-bio p + p{margin-top:1.264em}

.ab-title{position:absolute;left:6.406%;top:8.60%}
.ab-bio  {position:absolute;left:6.406%;top:14.15%;width:57%}
.ab-edu  {position:absolute;left:6.406%;top:39.96%;width:34%}
.ab-deg  {position:absolute;left:41.93%;top:39.96%;width:30%}
.ab-sk   {position:absolute;left:6.770%;top:63.37%}
.ab-lang {position:absolute;left:41.93%;top:63.37%;width:28%}

/* software tiles + proficiency bars */
.ab-tools{
  position:absolute;left:6.458%;top:69.56%;width:30.52%;
  display:grid;grid-template-columns:repeat(8,1fr);gap:1.95%;
}
.ab-tools li{list-style:none}
.ab-tools img{width:100%;display:block}
/* ---- proficiency tubes ----
   Not a progress bar: a little glass tube that fills with liquid. The wall is
   an inset ring over a tonal glass gradient, lit by a specular streak along
   the top; `i` is the cavity that clips, and `b` is the liquid itself, held
   inside the wall with a vertical gradient that gives it volume. The liquid
   is scaled, never resized — `scaleX` is composited, and squashing its round
   cap into an ellipse is exactly what a meniscus looks like. */
.ab-lvl{
  /* block, not the span default: an inline box ignores width and aspect-ratio */
  position:relative;display:block;
  width:91.7%;margin:12.5% 0 0 2.8%;aspect-ratio:66/24;
  border-radius:999px;
  background:linear-gradient(180deg,#EDEDF2 0%,#FFFFFF 42%,#E4E4EC 100%);
  box-shadow:
    inset 0 0 0 max(1px,.055cqw) color-mix(in srgb,var(--pdf-violet) 25%, transparent),
    inset 0 .12em .22em rgba(26,0,81,.13);
}
.ab-lvl::after{ /* the glass highlight riding the top wall */
  content:"";position:absolute;left:9%;right:9%;top:13%;height:20%;z-index:2;
  border-radius:999px;pointer-events:none;
  background:linear-gradient(180deg,rgba(255,255,255,.8),rgba(255,255,255,0));
}
.ab-lvl i{position:absolute;inset:11% 5%;border-radius:999px;overflow:hidden}
.ab-lvl b{
  position:absolute;inset:0;border-radius:999px;
  transform-origin:left center;
  transform:scaleX(calc(var(--f) / 100));
  background:linear-gradient(180deg,
    color-mix(in srgb,var(--pdf-violet) 58%, #9A92E4) 0%,
    var(--pdf-violet) 46%,
    color-mix(in srgb,var(--pdf-violet) 70%, #000) 100%);
  /* lit along its own surface, shaded where it pools: the liquid reads wet
     rather than printed. Both insets are horizontal bands, so scaling the
     column sideways never distorts them. */
  box-shadow:
    inset 0 .14em .16em rgba(255,255,255,.30),
    inset 0 -.12em .18em rgba(0,0,0,.34);
}
/* Liquid rushes in and rocks itself level, three diminishing swings. */
@keyframes lvl-fill{
  0%   {transform:scaleX(0)}
  62%  {transform:scaleX(calc(var(--f) / 100 * 1.055))}
  80%  {transform:scaleX(calc(var(--f) / 100 * .975))}
  92%  {transform:scaleX(calc(var(--f) / 100 * 1.012))}
  100% {transform:scaleX(calc(var(--f) / 100))}
}
/* The tiles land one after another (--d staggers them left to right, most
   known to least) and each tube then fills. Both states hang off .rv — a
   class only the reveal engine adds — so a visitor without JS, or on reduced
   motion, gets the finished board straight away. */
.ab-tools.rv li{
  opacity:0;transform:translateY(26%) scale(.8);
  transition:
    opacity calc(var(--anim-dur) * .6) ease-out,
    transform var(--anim-dur) cubic-bezier(.18,1.3,.36,1);
  transition-delay:calc(var(--anim-delay) + var(--d,0ms));
}
.ab-tools.rv.in li{opacity:1;transform:none}
.ab-tools.rv .ab-lvl b{transform:scaleX(0)}
.ab-tools.rv.in .ab-lvl b{
  animation:lvl-fill var(--anim-dur) cubic-bezier(.35,.85,.4,1) both;
  /* the tube starts filling once its own tile has landed */
  animation-delay:calc(var(--anim-delay) + var(--d,0ms) + var(--anim-step));
}

/* portrait column, bleeding off the right edge as it does in the PDF */
.ab-photo{
  position:absolute;left:70.63%;top:0;width:29.375%;height:100%;
  overflow:hidden;box-shadow:-30px 0 70px -25px rgba(26,0,81,.30);
}
.ab-photo img{width:100%;height:100%;object-fit:cover;object-position:top center}
/* The frame runs on behind the contact strip, which is why its mid handles
   sit at 64% of the visible run rather than halfway down it. */
.ab-frame{
  position:absolute;left:72.86%;top:6.845%;width:24.84%;height:84.55%;
  border:max(1.5px,.21cqw) solid var(--lime);border-bottom:0;
  pointer-events:none;
}
.ab-frame span{
  position:absolute;width:max(12px,2.03cqw);aspect-ratio:1;
  border:max(1.5px,.21cqw) solid var(--lime);
}
.ab-frame .t-l{left:0;top:0;transform:translate(-50%,-50%)}
.ab-frame .t-r{right:0;top:0;transform:translate(50%,-50%)}
.ab-frame .m-l{left:0;top:50%;transform:translate(-50%,-50%)}
.ab-frame .m-r{right:0;top:50%;transform:translate(50%,-50%)}

/* contact strip laid over the portrait */
.ab-contact{
  position:absolute;left:70.63%;top:83.90%;width:29.375%;height:11.47%;
  background:var(--pdf-contact);
  /* column split and inset taken from the PDF. Percentage padding on an
     absolutely positioned box resolves against its containing block, not
     its own width, so these are expressed in cqw instead. */
  display:grid;grid-template-columns:61.7% 38.3%;align-content:center;
  padding-left:2.19cqw;row-gap:.5cqw;
}
.ab-contact a{
  display:flex;align-items:center;gap:.35cqw;text-decoration:none;
  font-family:var(--font);font-weight:var(--w-data);font-size:max(.62rem,.87cqw);
  color:var(--pdf-violet);line-height:1;
}
.ab-contact a:hover{text-decoration:underline}
.ab-contact img{width:max(13px,1.25cqw);flex:none}

/* ============================================================
   BRAND VISUAL IDENTITY
   Each case study is its source PDF rendered whole, so the board on
   screen is the artboard Erica composed, not a reconstruction of it.
   ============================================================ */
.section-bvi{background:var(--black);padding-inline:0}
.section-bvi .plaque{max-width:min(92%,700px)}
.case-shot{
  width:min(calc(100% - 2 * var(--pad)), 1250px);
  margin-inline:auto;
}
.case-shot + .case-shot{margin-top:clamp(2.5rem,6vw,5rem)}
.case-shot img{width:100%;height:auto;display:block}

/* ---- the focus pull ----
   The only blocks on the page that opt out of the travelling reveal. They do
   not slide, scale or stagger: the whole board arrives as one piece, fading up
   while it resolves from soft to sharp. Both selectors carry .rv so the state
   only exists once the engine is running — without JS the board is simply
   there, in focus. Overrides .rv/.rv.in on specificity, not source order. */
.rv.rv-focus{
  opacity:0;
  transform:none;
  filter:blur(14px);
  transition:
    opacity var(--focus-dur) ease-out,
    filter var(--focus-dur) cubic-bezier(.22,.61,.25,1);
  transition-delay:var(--anim-delay);
  will-change:opacity,filter;
}
.rv.rv-focus.in{opacity:1;filter:blur(0)}

/* ============ CONTACT ============ */
.section-contact{background:var(--violet-deep);position:relative;overflow:hidden;padding:clamp(4rem,9vw,7rem) var(--pad) clamp(3rem,6vw,5rem)}
/* the slim footer every page carries */
.site-foot{background:var(--violet-ink);padding:clamp(2.5rem,5vw,3.5rem) var(--pad) 1.6rem;position:relative}
.big{
  /* the display role, shared with the cover wordmark: one giant statement per
     bookend, so the two must stay the same size */
  font-family:var(--font);font-weight:var(--w-display);color:var(--lime);
  font-size:clamp(2.3rem,8.4vw,6.6rem);line-height:1.02;letter-spacing:-.005em;text-transform:uppercase;
  margin-bottom:clamp(1rem,2.2vw,1.5rem);max-width:14ch;
}
/* The availability line: body role, so it stays the quiet voice under the
   statement rather than competing with it. It is held back with colour, not
   opacity — this element also carries a reveal, and `.rv.in{opacity:1}`
   outranks a plain `opacity` here and would wash the restraint away. */
.foot-lead{
  max-width:52ch;
  font-size:clamp(.95rem,1.6vw,1.06rem);line-height:1.7;
  color:color-mix(in srgb, var(--white) 72%, transparent);
  margin-bottom:clamp(2rem,5vw,3.5rem);
}
.contact-list{list-style:none;max-width:1250px;margin-inline:auto}
.contact-list li{border-top:1px solid color-mix(in srgb,var(--lime) 25%, transparent)}
.contact-list li:last-child{border-bottom:1px solid color-mix(in srgb,var(--lime) 25%, transparent)}
.contact-list a{
  display:flex;justify-content:space-between;align-items:center;gap:1rem;
  padding:1.3rem .2rem;text-decoration:none;transition:padding .25s, background .25s;
}
.contact-list a:hover{background:color-mix(in srgb,var(--lime) 6%, transparent);padding-inline:1rem}
.contact-list .label{font-size:.75rem;letter-spacing:.2em;text-transform:uppercase;opacity:.55;min-width:7.5rem}
/* The contact values carry a touch more weight than the labels beside them:
   with one family, that step is the whole distinction between them. */
.contact-list .value{
  font-family:var(--font);font-weight:var(--w-data);font-size:clamp(.9rem,2.5vw,1.45rem);
  flex:1;word-break:break-all;
}
.contact-list .arrow{font-size:1.3rem;color:var(--lime);transition:transform .25s}
.contact-list a:hover .arrow{transform:translate(6px,-6px)}
.foot-meta{
  max-width:1250px;margin:3rem auto 0;display:flex;justify-content:space-between;
  gap:1rem;flex-wrap:wrap;font-size:.75rem;opacity:.5;letter-spacing:.05em;
}

/* ============ LIGHTBOX ============ */
dialog#lb{
  border:0;padding:0;background:transparent;max-width:min(92vw,1100px);width:auto;
  margin:auto;
}
dialog#lb::backdrop{background:rgba(10,8,25,.92);backdrop-filter:blur(6px)}
#lb figure{position:relative;display:flex;flex-direction:column;align-items:center;gap:.9rem}
#lb img{max-height:82vh;width:auto;max-width:100%;border-radius:14px}
#lb figcaption{color:var(--lime);font-family:var(--font);font-weight:var(--w-data);font-size:.85rem;letter-spacing:.05em}
#lb .lb-btn{
  position:fixed;top:50%;transform:translateY(-50%);
  background:var(--lime);color:var(--violet);border:0;cursor:pointer;
  width:44px;height:44px;border-radius:50%;font-size:1.2rem;font-weight:700;
}
#lb .prev{left:2vw} #lb .next{right:2vw}
#lb .close{
  position:fixed;top:1.2rem;right:1.5rem;background:var(--lime);color:var(--violet);
  border:0;cursor:pointer;width:44px;height:44px;border-radius:50%;font-size:1.1rem;font-weight:700;
}

/* ============ RESPONSIVE ============ */
@media (max-width:900px){
  /* the About board leaves its fixed artboard and becomes a normal stack */
  .ab{aspect-ratio:auto;container-type:normal;padding:clamp(2.5rem,8vw,4rem) var(--pad) 0}
  .ab-title,.ab-bio,.ab-edu,.ab-deg,.ab-sk,.ab-lang,.ab-tools,.ab-photo,.ab-contact{
    position:static;left:auto;top:auto;width:auto;height:auto;
  }
  .ab-h{font-size:clamp(1.15rem,4.4vw,1.7rem)}
  .ab-p{font-size:clamp(.86rem,2.9vw,1rem);line-height:1.5}
  .ab-title{margin-bottom:.9rem}
  .ab-bio,.ab-edu,.ab-deg,.ab-sk,.ab-lang{margin-bottom:clamp(1.6rem,5vw,2.4rem)}
  .ab-sk{margin-bottom:.9rem}
  .ab-tools{width:min(100%,420px);gap:2.4%;margin-bottom:clamp(1.8rem,6vw,2.6rem)}
  .ab-photo{
    box-shadow:none;max-width:420px;margin:0 auto;aspect-ratio:3/4;
    border-radius:6px;
  }
  .ab-frame{display:none}
  .ab-contact{
    max-width:420px;margin:0 auto;padding:clamp(.9rem,3.5vw,1.3rem);
    grid-template-columns:1fr;row-gap:.75rem;
  }
  .ab-contact a{font-size:clamp(.78rem,2.7vw,.92rem);gap:.6rem}
  .ab-contact img{width:1.15rem}
}
@media (max-width:760px){
  nav.site-nav ul{gap:.7rem}
  nav.site-nav ul a,.nav-toggle{font-size:.72rem}
  nav.site-nav .nav-drop a{font-size:.78rem}
  .grid-3,.grid-posters{grid-template-columns:repeat(2,1fr)}
  .hero-box h1{white-space:normal;word-break:break-word}
  .hero-badge{right:0}          /* keep the overhang inside the viewport */
}
@media (max-width:480px){
  .grid-3,.grid-posters{grid-template-columns:1fr}
  nav.site-nav .brand span.full{display:none}
  /* anchor the menu to the right edge so it cannot hang off-screen */
  nav.site-nav .nav-drop{left:auto;right:0;translate:0 0;min-width:11.5rem}
  .plaque .box{padding-inline:clamp(1.1rem,4vw,1.6rem)}
}


/* ============================================================
   MULTI-PAGE CHROME
   Added when the one-pager became a site. Everything here is
   shared by every page — the identity has one definition.
   ============================================================ */

/* interior pages start under the fixed nav rather than behind it */
.page-top{padding-top:calc(5.5rem + clamp(1.5rem,4vw,3rem))}

/* current page in the nav: lime, and not signalled by colour alone */
nav.site-nav a[aria-current="page"]{opacity:1;color:var(--lime)}
nav.site-nav ul > li > a[aria-current="page"]{text-decoration:underline;text-underline-offset:6px;text-decoration-thickness:2px}
nav.site-nav .nav-drop a[aria-current="page"]{background:color-mix(in srgb,var(--lime) 16%, transparent);color:var(--lime);opacity:1}
.nav-toggle[data-here]{color:var(--lime);opacity:1}

/* one short line under a plaque */
.page-lead{
  max-width:56ch;margin:calc(-1 * clamp(1rem,2.5vw,2rem)) auto clamp(2.5rem,6vw,4rem);
  text-align:center;font-size:clamp(.95rem,1.6vw,1.06rem);line-height:1.7;
  color:color-mix(in srgb, var(--white) 70%, transparent);
}

/* ---- home: the three ways into the work ---- */
.cat-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(.9rem,2vw,1.6rem)}
.cat-card{
  position:relative;display:block;text-decoration:none;color:inherit;
  border-radius:var(--radius);overflow:hidden;background:var(--violet-deep);
  aspect-ratio:4/5;
}
.cat-card img{width:100%;height:100%;object-fit:cover;transition:transform .6s ease,opacity .4s ease;opacity:.62}
.cat-card:hover img,.cat-card:focus-visible img{transform:scale(1.05);opacity:.8}
.cat-card .veil{
  position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(180deg,transparent 28%,color-mix(in srgb,var(--violet-ink) 94%, transparent) 100%);
}
.cat-card .body{position:absolute;inset:auto 0 0 0;padding:clamp(1.1rem,2.6vw,1.8rem)}
.cat-card .idx{
  display:block;margin-bottom:.5rem;
  font-family:var(--font);font-weight:var(--w-strong);font-size:.8rem;
  letter-spacing:.02em;color:var(--lime);opacity:.6;
}
.cat-card h3{
  font-family:var(--font);font-weight:var(--w-display);color:var(--lime);
  font-size:clamp(1rem,2.1vw,1.5rem);line-height:1.15;text-transform:uppercase;
}
.cat-card p{margin-top:.55rem;font-size:.82rem;line-height:1.55;opacity:.72;max-width:26ch}
.cat-card .go{
  position:absolute;top:clamp(.9rem,2vw,1.3rem);right:clamp(.9rem,2vw,1.3rem);
  width:2.2rem;height:2.2rem;border-radius:50%;display:grid;place-items:center;
  background:var(--lime);color:var(--violet);font-weight:700;
  transform:translate(6px,-6px) scale(.7);opacity:0;
  transition:transform .3s cubic-bezier(.18,1.3,.36,1), opacity .3s ease;
}
.cat-card:hover .go,.cat-card:focus-visible .go{transform:none;opacity:1}
/* the same selection frame the artworks use, so the family holds */
.cat-card .sel{
  position:absolute;inset:8px;border:1.5px solid var(--lime);opacity:0;
  transition:opacity .25s;pointer-events:none;
  background-image:
    linear-gradient(var(--lime),var(--lime)),linear-gradient(var(--lime),var(--lime)),
    linear-gradient(var(--lime),var(--lime)),linear-gradient(var(--lime),var(--lime));
  background-size:9px 9px;background-repeat:no-repeat;
  background-position:top left,top right,bottom left,bottom right;
}
.cat-card:hover .sel,.cat-card:focus-visible .sel{opacity:1}

/* ---- home: the short introduction ---- */
.intro{display:grid;grid-template-columns:minmax(0,1.35fr) minmax(0,1fr);gap:clamp(1.5rem,4vw,3.5rem);align-items:center}
.intro p{font-size:clamp(1rem,1.8vw,1.22rem);line-height:1.75;max-width:46ch}
.intro p + p{margin-top:1.1rem}
.intro .actions{display:flex;flex-wrap:wrap;gap:.9rem;margin-top:1.8rem}
/* the introduction photo is landscape, so the frame is too — cropping it to a
   portrait slot would cut the hands, which are the point of the picture */
.intro .portrait{border-radius:var(--radius);overflow:hidden;aspect-ratio:3/2}
.intro .portrait img{width:100%;height:100%;object-fit:cover;object-position:center}

/* ---- shared button ---- */
.btn{
  display:inline-flex;align-items:center;gap:.6em;
  font-family:var(--font);font-weight:var(--w-strong);font-size:.85rem;
  letter-spacing:.08em;text-transform:uppercase;text-decoration:none;
  background:var(--lime);color:var(--violet);border:0;cursor:pointer;
  padding:.95em 1.7em;border-radius:999px;
  box-shadow:5px 5px 0 var(--black);
  transition:transform .18s ease, box-shadow .18s ease;
}
.btn:hover,.btn:focus-visible{transform:translate(3px,3px);box-shadow:2px 2px 0 var(--black)}
.btn.ghost{background:none;color:var(--lime);box-shadow:none;border:1.5px solid var(--lime)}
.btn.ghost:hover,.btn.ghost:focus-visible{transform:none;background:color-mix(in srgb,var(--lime) 14%, transparent)}

/* ---- contact form ---- */
.contact-cols{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:clamp(2rem,5vw,4.5rem);align-items:start}
.form{display:grid;gap:clamp(1rem,2.2vw,1.35rem)}
.form .row{display:grid;grid-template-columns:1fr 1fr;gap:inherit}
.field{display:grid;gap:.45rem}
.field label{font-size:.75rem;letter-spacing:.2em;text-transform:uppercase;opacity:.65;font-weight:500}
.field input,.field textarea{
  font-family:var(--font);font-weight:var(--w-body);font-size:1rem;color:var(--white);
  background:color-mix(in srgb,var(--black) 45%, transparent);
  border:1.5px solid color-mix(in srgb,var(--lime) 22%, transparent);
  border-radius:12px;padding:.85rem 1rem;width:100%;
  transition:border-color .2s, background .2s;
}
.field textarea{min-height:9rem;resize:vertical;line-height:1.6}
.field input:hover,.field textarea:hover{border-color:color-mix(in srgb,var(--lime) 40%, transparent)}
.field input:focus,.field textarea:focus{
  border-color:var(--lime);
  background:color-mix(in srgb,var(--black) 62%, transparent);
}
.field .err{font-size:.78rem;color:#FFB3C8;min-height:1.15em}
.field[data-invalid] input,.field[data-invalid] textarea{border-color:#FF7BA6}
.form-note{font-size:.82rem;line-height:1.6;opacity:.6;max-width:52ch}
.form-status{
  font-size:.9rem;line-height:1.6;padding:.9rem 1.1rem;border-radius:12px;
  background:color-mix(in srgb,var(--lime) 12%, transparent);
  border:1px solid color-mix(in srgb,var(--lime) 35%, transparent);
  color:var(--lime);
}
.form-status[hidden]{display:none}

/* ---- slim site footer ---- */
.site-foot .inner{
  max-width:1250px;margin-inline:auto;display:flex;flex-wrap:wrap;
  gap:1.4rem 2.5rem;align-items:center;justify-content:space-between;
}
.site-foot .brand{font-family:var(--font);font-weight:var(--w-display);color:var(--lime);font-size:1.05rem;text-decoration:none}
.site-foot .links{display:flex;flex-wrap:wrap;gap:.9rem 1.5rem}
.site-foot .links a{
  font-size:.78rem;letter-spacing:.12em;text-transform:uppercase;
  text-decoration:none;opacity:.7;transition:opacity .2s,color .2s;
}
.site-foot .links a:hover{opacity:1;color:var(--lime)}
.site-foot .foot-meta{margin-top:2rem}

@media (max-width:900px){
  .cat-grid{grid-template-columns:1fr;max-width:420px;margin-inline:auto}
  .intro{grid-template-columns:1fr;gap:2rem}
  .intro .portrait{max-width:460px;margin-inline:auto}
  .contact-cols{grid-template-columns:1fr;gap:2.5rem}
}
@media (max-width:600px){
  .form .row{grid-template-columns:1fr}
  .site-foot .inner{flex-direction:column;align-items:flex-start}
}

/* keyboard users get past the fixed nav in one tab */
.skip{
  position:absolute;left:50%;translate:-50% 0;top:-100%;z-index:130;
  background:var(--lime);color:var(--violet);
  font-family:var(--font);font-weight:var(--w-strong);font-size:.8rem;
  letter-spacing:.08em;text-transform:uppercase;text-decoration:none;
  padding:.7rem 1.3rem;border-radius:0 0 12px 12px;
  transition:top .18s ease;
}
.skip:focus-visible{top:0}


/* ============================================================
   ABOUT — THE ONE ROOM THAT IS NOT THE STUDIO
   Every other page is Erica Rueda, the professional: rigid grid,
   dark ground, no ornament. This page is hers. It keeps the type
   and the palette — that is what stops it looking like a different
   website — and breaks everything else: the ground goes to paper,
   the cards are hand-placed and tilted, and ornament is allowed
   here and nowhere else. See The One-Room Rule in DESIGN.md.
   ============================================================ */

/* ---- the scrap palette ----
   Erica's own colours, and the only place on the site they are allowed.
   Three of them were sampled straight out of the header video, where she
   had already drawn marker outlines over her own footage — the doodles on
   this page match her handwriting because they are literally her colours,
   not a guess at them. Scoped to .is-me so they cannot leak into the
   violet/lime system: The One-Room Rule applies to colour too. */
.is-me{
  --sc-pink:#FF4FA3;
  --sc-red:#D90038;      /* sampled from about-intro.mp4 */
  --sc-yellow:#F1BD38;   /* sampled from about-intro.mp4 */
  --sc-blush:#EB7994;    /* sampled from about-intro.mp4 */
  --sc-mint:#63D3A6;
  --sc-aqua:#4EC5E0;
  --sc-orange:#FF8A3D;
  /* The board itself, Erica's choice: pastel pink instead of the PDF's
     #CCCCD2 grey. This is the one page where the source board is not the
     authority — the room is. Deep enough that the white cards still sit
     off it (1.3:1, against 1.55:1 on the old grey; the solid violet offset
     shadow is what actually separates a card, not the luminance step). */
  --sc-board:#F5D3E1;
}

/* ---- the video header ---- */
.me-hero{
  position:relative;overflow:hidden;
  min-height:min(62svh,560px);
  display:flex;align-items:flex-end;
  padding:calc(5.5rem + clamp(1.5rem,4vw,3rem)) var(--pad) clamp(2rem,5vw,3.5rem);
  background:var(--black);
}
.me-video{
  position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;object-position:center;
}
.me-veil{
  position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(180deg,
    color-mix(in srgb,var(--black) 64%, transparent) 0%,
    color-mix(in srgb,var(--black) 52%, transparent) 40%,
    color-mix(in srgb,var(--black) 74%, transparent) 72%,
    color-mix(in srgb,var(--black) 90%, transparent) 100%);
}
/* The header's own pause control shares .vid-btn's look and only overrides
   its place and size. Qualified by .me-hero on purpose: .vid-btn is defined
   further down the file, so an unqualified rule here would lose the width
   and height to it on source order. */
.me-hero .me-vidbtn{top:calc(5.5rem + .6rem);right:var(--pad);width:2.4rem;height:2.4rem}
.me-hero-in{position:relative;z-index:2;max-width:1250px;margin-inline:auto;width:100%}
.me-title{
  font-family:var(--font);font-weight:var(--w-display);color:var(--lime);
  font-size:clamp(2.4rem,10vw,7rem);line-height:.92;text-transform:uppercase;
  letter-spacing:.01em;
}
/* the second word drops and tilts — the first break of the grid */
.me-title em{
  display:inline-block;font-style:normal;color:var(--white);
  transform:rotate(-4deg) translateY(.06em);margin-left:.12em;
}
/* No measure cap: this line is meant to sit under the title as a single
   line rather than break after "just". It only wraps once the viewport is
   genuinely too narrow for it, which is the right place to wrap. */
.me-kicker{
  margin-top:clamp(.8rem,2vw,1.2rem);
  font-size:clamp(.95rem,1.6vw,1.06rem);line-height:1.7;
  color:color-mix(in srgb,var(--white) 78%, transparent);
}

/* ---- the scrapbook ----
   Two independent columns, each a flex stack, instead of the twelve-column
   grid this used to be. The old grid auto-placed cards into a 12-col field,
   and because the left column ran out of cards before the tall photo column
   did, the last two rows opened a dead half-width hole beside Languages.
   Columns that own their own children cannot leave a hole between cards:
   whatever is left over falls at the foot of the shorter column, where the
   doodles live. Never go back to auto-placement here. */
.me{
  position:relative;background:var(--sc-board);color:var(--pdf-violet);
  padding-block:clamp(3rem,7vw,5.5rem);padding-inline:var(--pad);
  overflow:hidden;   /* keeps the doodles that hang past the edge from scrolling the page */
}
.me-wrap{
  position:relative;max-width:1250px;margin-inline:auto;
  display:grid;grid-template-columns:minmax(0,1.18fr) minmax(0,1fr);
  gap:clamp(1.2rem,2.6vw,2.1rem);align-items:stretch;
}
/* The columns stretch to the taller of the two and hand whatever is left
   over to the spaces BETWEEN the cards, not to the cards themselves. The
   first attempt let the closing print grow to absorb the slack instead,
   which did end the board level — and blew that photograph up to 700px and
   cropped her out of it. Slack belongs in the gaps: a scrapbook is allowed
   loose spacing, and the doodles underneath show through the extra room. */
.me-col{
  display:flex;flex-direction:column;justify-content:space-between;
  gap:clamp(1.2rem,2.6vw,2.1rem);min-width:0;
}
/* A corner sticker on a print clears the frame rather than sitting on the
   photograph — a print is the one card whose whole surface is content. */
.scrap > .sticker.st-tr,.scrap > .sticker.st-tl{top:-1.5rem}
.scrap > .sticker.st-br,.scrap > .sticker.st-bl{bottom:-1.2rem}

/* Languages runs three across, not three stacked: it spans the full column
   now, and a stacked list left the right half of the card empty. */
.me-lang .lang-row{list-style:none;display:grid;grid-template-columns:repeat(3,1fr);
  gap:clamp(.5rem,1.4vw,1.1rem);margin-top:.6rem}
.me-lang .lang-row li{font-size:clamp(.86rem,1.35vw,1rem);line-height:1.5}
.me-lang .lang-row b{display:block}

/* the WhatsApp line under the button on the closing card */
.me-say-alt{margin-top:.9rem;font-size:.72rem;letter-spacing:.04em}
.me-say-alt a{color:var(--pdf-violet);text-underline-offset:3px}

/* ---- pinned marks ----
   The front-layer doodles are children of the card they are drawn on rather
   than absolute percentages of the page. Percentages put one on her arm and
   one across her leg the moment the layout moved; a corner stays a corner. */
.sc.pin{transform:rotate(var(--r,0deg));z-index:6}
.pin-tl{left:-.7rem;top:-1rem;right:auto}
.pin-tr{right:-1rem;top:-.8rem;left:auto}
.pin-bl{left:-1rem;bottom:-.7rem;top:auto}
.pin-br{right:-.9rem;bottom:-.5rem;left:auto;top:auto}
/* two cards sharing a line inside a column */
.me-duo{display:grid;grid-template-columns:1fr 1fr;gap:clamp(1rem,2.2vw,1.8rem);align-items:start}
.me-say{grid-column:1 / -1}
/* every card sits above the doodle layer; the gaps between them are where
   the collage shows through */
.me-card,.me-photos,.me-vid,.scrap{position:relative;z-index:2}

.me-card{
  position:relative;
  background:#FBFBFD;border-radius:14px;
  padding:clamp(1.2rem,2.6vw,1.9rem);
  box-shadow:7px 7px 0 color-mix(in srgb,var(--pdf-violet) 88%, transparent);
  transform:rotate(var(--tilt,0deg));
}
.me-card .ab-h{font-size:clamp(1rem,1.7vw,1.25rem);margin-bottom:.5rem}
.me-card .ab-p{font-size:clamp(.86rem,1.35vw,1rem);line-height:1.6}
.me-card .ab-p + .ab-p{margin-top:.9rem}
.me-bio .ab-p{font-size:clamp(.95rem,1.55vw,1.12rem);line-height:1.7}

/* ---- washi tape ----
   A strip holding each card down. The colour and the weave are per-strip:
   a roll of tape is the cheapest texture on the board, and it can never
   cost legibility because a strip only ever lands on a card corner.
   Everywhere else on the site tape does not exist at all. */
.tape{
  position:absolute;z-index:3;
  width:clamp(52px,7vw,86px);height:clamp(18px,2.2vw,26px);
  background:color-mix(in srgb,var(--tape-col,var(--lime)) 62%, transparent);
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--pdf-violet) 12%, transparent);
}
.tape-a{top:-.6rem;left:8%;transform:rotate(-4deg)}
.tape-b{top:-.7rem;right:9%;transform:rotate(5deg)}
.t-pink{--tape-col:var(--sc-pink)}
.t-aqua{--tape-col:var(--sc-aqua)}
.t-sun {--tape-col:var(--sc-yellow)}
.t-mint{--tape-col:var(--sc-mint)}
.t-orange{--tape-col:var(--sc-orange)}
/* the weave: declared after the background shorthand above, so it layers
   over the colour rather than replacing it */
.tape.striped{
  background-image:repeating-linear-gradient(114deg,
    rgba(255,255,255,.6) 0 5px, transparent 5px 11px);
}
.tape.dotted{
  background-image:radial-gradient(circle at 3.5px 3.5px, rgba(255,255,255,.65) 1.7px, transparent 1.8px);
  background-size:9px 9px;
}

/* ---- stickers: her own words, hand-stuck ----
   Each sticker is a child of the card it is stuck to and is positioned
   against that card's own corners. They used to hang off .me-wrap at
   percentages of the whole page, which meant any change in card height slid
   them somewhere new — that is how the pink one ended up sitting on top of
   the Canva icon. A sticker must never be positioned against the page. */
.sticker{
  position:absolute;z-index:6;pointer-events:auto;
  font-family:var(--font);font-weight:var(--w-strong);
  font-size:clamp(.62rem,1vw,.75rem);letter-spacing:.02em;text-transform:uppercase;
  padding:.55em 1em;border-radius:999px;white-space:nowrap;
  background:var(--lime);color:var(--pdf-violet);
  box-shadow:3px 3px 0 var(--pdf-violet);
  transform:rotate(var(--srot,0deg));
  transition:transform .28s cubic-bezier(.18,1.3,.36,1);
}
.sticker:hover{transform:rotate(var(--srot,0deg)) scale(1.08)}
.sticker.pinkie{background:var(--sc-pink);color:#fff}
.sticker.sunny {background:var(--sc-yellow);color:var(--pdf-violet)}
/* corner anchors — a sticker only ever takes a corner, never a percentage */
.st-tr{top:-.85rem;right:1.4rem}
.st-tl{top:-.85rem;left:1.4rem}
.st-br{bottom:-.85rem;right:1.4rem}
.st-bl{bottom:-.85rem;left:1.4rem}
/* the long one wraps rather than pushing past the card it belongs to */
.sticker.wrap{white-space:normal;max-width:9.5rem;text-align:center;line-height:1.35}

/* the tubes keep their own board grey behind them. The top padding is
   structural: it is the clearance that guarantees a corner sticker can
   never reach the first icon in the row. */
.me-skills .ab-tools{
  position:static;width:100%;margin-top:1.15rem;padding-top:.35rem;
  display:grid;grid-template-columns:repeat(8,1fr);gap:2.4%;
}

/* ---- photo scraps: everything on this page is a print, taped down ----
   A white border with a deep bottom margin and a caption sitting in it —
   a print out of an album, not a web thumbnail. The tilt is per-instance
   (--tilt) so no two sit square, and the solid offset shadow is the
   system's, which is what keeps a scrapbook photo related to a Work card. */
.scrap{
  background:#fff;padding:.5rem .5rem 1.8rem;border-radius:2px;
  box-shadow:0 1px 2px rgba(20,16,46,.18),
             7px 8px 0 color-mix(in srgb,var(--pdf-violet) 82%, transparent);
  transform:rotate(var(--tilt,0deg));
}
.scrap > img,.scrap video{
  width:100%;aspect-ratio:var(--ar,4/5);object-fit:cover;
  object-position:var(--pos,center);display:block;background:var(--black);
}
/* Sora ships no italic, and a synthesised oblique on a designer's own page
   is exactly the tell to avoid — so the caption separates itself from the
   photograph by tracking and weight instead of by slant. */
.scrap .cap{
  position:absolute;left:.5rem;right:.5rem;bottom:.45rem;
  text-align:center;font-weight:var(--w-data);font-size:.72rem;letter-spacing:.09em;
  color:color-mix(in srgb,var(--pdf-violet) 74%, transparent);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}

/* ---- the video scrap: her talking, muted, in the same frame as the photos ---- */
.me-vid .scrap{padding-bottom:1.8rem}
.vid-btn{
  position:absolute;z-index:4;
  width:2.2rem;height:2.2rem;border-radius:50%;
  display:grid;place-items:center;
  background:color-mix(in srgb,var(--black) 58%, transparent);
  color:var(--lime);border:1px solid color-mix(in srgb,var(--lime) 40%, transparent);
  font-size:.62rem;cursor:pointer;transition:background .2s,transform .2s,color .2s;
}
.vid-btn:hover{background:var(--lime);color:var(--violet);transform:scale(1.06)}
.me-vid .vid-btn{right:.95rem;bottom:2.25rem}

/* ---- the closing band ----
   This used to be a bare line of text and a button floating in a half-width
   card, which is why it read as unfinished. It is now the full width of the
   board and flanked by two prints, so the page ends on her rather than on
   an empty column. */
.me-say{
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,auto);
  gap:clamp(1.2rem,3.4vw,3rem);align-items:center;
  padding:clamp(1.4rem,3vw,2.4rem) clamp(1.2rem,3vw,2.6rem);
  text-align:center;
}
.me-say-in{min-width:0}
.me-say-t{
  font-family:var(--font);font-weight:var(--w-display);text-transform:uppercase;line-height:1;
  font-size:clamp(1.6rem,4.6vw,3rem);color:var(--pdf-violet);
  margin-bottom:.7rem;
}
.me-say-p{font-size:clamp(.95rem,1.55vw,1.12rem);line-height:1.6;margin-bottom:1.2rem}
.me-say .scrap{width:clamp(120px,15vw,190px)}
/* the one button on the board takes her pink instead of the system lime —
   violet on #FF4FA3 measures 5.9:1, so it stays legible while it stops
   looking like the studio's CTA */
.me-say .btn{background:var(--sc-pink);color:var(--pdf-violet)}

/* ---- the carousel ---- */
.me-photos{position:relative}
.carousel{
  position:relative;overflow:hidden;background:var(--black);
}
.c-track{
  display:flex;margin:0;padding:0;list-style:none;
  transition:transform .62s cubic-bezier(.22,1,.36,1);
}
.c-slide{flex:0 0 100%;aspect-ratio:4/5;position:relative}
.c-slide img{width:100%;height:100%;object-fit:cover;display:block}
.c-arrow{
  position:absolute;top:50%;translate:0 -50%;z-index:3;
  width:2.5rem;height:2.5rem;border-radius:50%;border:0;cursor:pointer;
  display:grid;place-items:center;font-size:1rem;font-weight:700;
  background:color-mix(in srgb,var(--black) 55%, transparent);color:var(--lime);
  transition:background .2s,color .2s,transform .2s;
}
.c-arrow:hover{background:var(--lime);color:var(--violet)}
.c-prev{left:.7rem} .c-next{right:.7rem}
.c-arrow[disabled]{opacity:.3;cursor:default}
.c-arrow[disabled]:hover{background:color-mix(in srgb,var(--black) 55%, transparent);color:var(--lime)}
.c-dots{
  position:absolute;left:0;right:0;bottom:.9rem;z-index:3;
  display:flex;justify-content:center;gap:.45rem;
}
.c-dots button{
  width:.5rem;height:.5rem;border-radius:50%;border:0;padding:0;cursor:pointer;
  background:color-mix(in srgb,#fff 45%, transparent);
  transition:background .25s, transform .25s;
}
.c-dots button[aria-selected="true"]{background:var(--lime);transform:scale(1.35)}
/* Floating over the photo when the carousel has no frame to write in;
   the .cap rule takes over when it sits in a print's white margin. */
.c-count{
  position:absolute;top:.7rem;left:.8rem;z-index:3;
  font-size:.72rem;letter-spacing:.08em;
  background:color-mix(in srgb,var(--black) 55%, transparent);color:var(--lime);
  padding:.25em .7em;border-radius:999px;
}
.c-count.cap{
  top:auto;left:.5rem;right:.5rem;background:none;padding:0;letter-spacing:.12em;
  /* .c-count is declared after .cap, so the lime has to be undone here or it
     would win on source order and go invisible on the white margin */
  color:color-mix(in srgb,var(--pdf-violet) 74%, transparent);
}

/* ============================================================
   THE DOODLE LAYER — the collage, and where it is allowed to be
   Loose marker marks in her own colours, scattered across the board.
   Two layers, and the difference between them is the whole safety
   argument: the back layer (z-index 1) sits UNDER the opaque cards, so
   however boldly it is placed it can never touch a line of body copy —
   it shows through the gaps only. The front layer (z-index 5) rides
   over the cards and is hand-placed on photographs and card corners,
   never over text. Anything new goes in the back layer unless you have
   checked what is underneath it.
   ============================================================ */
.me-scraps{position:absolute;inset:0;z-index:1;pointer-events:none;overflow:hidden}
.me-scraps.front{z-index:5}
.sc{
  position:absolute;left:var(--x);top:var(--y);width:var(--w,120px);
  transform:translate(-50%,-50%) rotate(var(--r,0deg));
}
.sc svg{
  display:block;width:100%;height:auto;overflow:visible;
  fill:none;stroke-linecap:round;stroke-linejoin:round;
  /* The wobble that stops these reading as vector clip-art: a turbulence
     displacement pushes every stroke off its own path by a couple of
     pixels, so the line breathes the way a marker line does. The filter is
     on the <svg>, never on .sc — the drift animation below transforms the
     filtered result instead of re-running the filter every frame. */
  filter:url(#rough);
}
/* ---- the marks are never quite still ----
   Composed with the individual `translate`/`rotate` properties rather than
   `transform`, which .sc is already using for its own centring and tilt.
   The individual properties apply on top of `transform`, so the two do not
   fight and a pinned mark and a placed mark can share one keyframe. */
@keyframes sc-drift{
  0%,100%{translate:0 0;rotate:0deg}
  50%    {translate:var(--dx,0) var(--dy,-7px);rotate:var(--dr,3deg)}
}
.sc{animation:sc-drift var(--fd,7s) ease-in-out infinite}
/* every third mark drifts differently, and the negative delays start them
   mid-cycle so they never breathe in unison */
.sc:nth-child(3n)  {--fd:8.6s;--dr:-2.5deg;--dy:5px;--dx:3px}
.sc:nth-child(3n+1){--fd:6.4s;--dr:3.5deg;--dy:-6px;--dx:-2px}
.sc:nth-child(3n+2){--fd:10.2s;--dr:-4deg;--dy:-4px;--dx:2px}
.sc:nth-child(4n)  {animation-delay:-2.2s}
.sc:nth-child(5n)  {animation-delay:-3.7s}
.sc:nth-child(7n)  {animation-delay:-5.1s}

/* ---- stickers that never settle ----
   Only the two marked .alive; a board where everything twitches is noise. */
@keyframes st-bob{
  0%,100%{translate:0 0;rotate:0deg}
  34%    {translate:0 -4px;rotate:-3.5deg}
  67%    {translate:0 2px;rotate:2.5deg}
}
.sticker.alive{animation:st-bob 3.6s ease-in-out infinite}
.sticker.alive:nth-of-type(even){animation-duration:4.4s;animation-delay:-1.3s}
/* hover hands control back to the pointer */
.sticker.alive:hover{animation:none}
/* The marks draw themselves in, on the shared beat. Default state is
   DRAWN: JS arms the hidden state, so a visitor without JS gets the
   finished collage rather than a blank board. */
.sc svg :is(path,circle,ellipse,line,polyline){
  transition:stroke-dashoffset var(--anim-dur) cubic-bezier(.22,1,.36,1);
  transition-delay:calc(var(--anim-delay) + var(--d,0ms));
}
.me.draw-armed .sc svg :is(path,circle,ellipse,line,polyline){
  stroke-dashoffset:var(--len,300)
}
.me.draw-armed.drawn .sc svg :is(path,circle,ellipse,line,polyline){
  stroke-dashoffset:0
}

/* ============================================================
   THE PAINT TRAIL — About only
   The brush actually paints here. Strokes are laid down under the
   pointer and erased again within about a second, so the visitor can
   draw over her page without ever defacing it. Fixed to the viewport
   because the marks are gone long before a scroll could carry them.
   Multiply keeps dark type legible through a pastel wash; it darkens
   the ground under the text rather than veiling the text itself.
   Sits under the nav (z-index 110) so chrome is never painted over.
   ============================================================ */
#paint{
  position:fixed;inset:0;z-index:7;pointer-events:none;
  mix-blend-mode:multiply;
}

/* ---- the pop-in: cards arrive tilted, not sliding ---- */
.rv.rv-pop{
  opacity:0;
  transform:translate3d(0,34px,0) scale(.9) rotate(calc(var(--tilt,0deg) - 6deg));
  transition:
    opacity calc(var(--anim-dur) * .5) ease-out,
    transform var(--anim-dur) cubic-bezier(.18,1.3,.36,1);
  transition-delay:calc(var(--anim-delay) + var(--rdel,0ms));
}
.rv.rv-pop.in{opacity:1;transform:rotate(var(--tilt,0deg))}
/* the stickers land after the cards they sit on */
.me-wrap .sticker{opacity:0;transition:opacity .5s ease, transform .6s cubic-bezier(.18,1.3,.36,1);}
.me-wrap.stuck .sticker{opacity:1}

@media (max-width:900px){
  .me-wrap{grid-template-columns:1fr}
  .me-photos{max-width:440px;margin-inline:auto;width:100%}
  /* the closing band unstacks into a single centred column, with the print
     under the button rather than beside it */
  .me-say{grid-template-columns:1fr;gap:1.6rem;padding-inline:0}
  .me-say .scrap{width:min(52%,200px);margin-inline:auto}
  .me-say-in{order:-1}
  /* the sticker moves off the corner the print now occupies */
  .me-say .sticker{left:50%;right:auto;bottom:-1.1rem;transform:translateX(-50%) rotate(var(--srot,0deg))}
  .me-say .sticker:hover{transform:translateX(-50%) rotate(var(--srot,0deg)) scale(1.08)}
  /* Stacked cards leave nowhere for a sticker to hang: one hanging off the
     bottom of a card lands on the tape of the card below it. On a phone the
     bottom ones tuck inside the card they belong to instead. Scoped to
     .me-col so the closing band keeps its own centred placement. */
  /* …and the room for them comes from the gap, not from tucking them inside.
     Moving a sticker into the card just trades one collision for another:
     inside a text card it lands on the heading, inside a print it lands on
     the photograph. A wider gap lets them keep hanging where they belong. */
  .me-col{gap:2.7rem}
  /* a narrow column cannot carry a full collage: only the marks with room
     to breathe survive, and the tilts flatten so the stack still reads */
  .sc.wide-only{display:none}
  .me-card,.scrap{--tilt:0deg}
  .me-title em{margin-left:0}
}
@media (max-width:560px){
  .me-card{box-shadow:5px 5px 0 color-mix(in srgb,var(--pdf-violet) 88%, transparent)}
  .me-skills .ab-tools{grid-template-columns:repeat(4,1fr);gap:5%;row-gap:1.2rem}
  /* the pair unstacks, so its own gap has to clear a hanging sticker too */
  .me-duo{grid-template-columns:1fr;gap:2.7rem}
}
