/* ==========================================================================
   The Ramma — holding page
   --------------------------------------------------------------------------
   Palette is sampled from the goods themselves rather than invented: the
   canvas, the lino ink, the logo's circle, the strap webbing, and the oxblood
   tag stitched onto every bag.

   Everything is styled through tokens, never inside a media query, so the
   OS preference and the manual [data-theme] override stay in agreement.
   ========================================================================== */

:root {
  color-scheme: light dark;

  /* --- light: raw canvas ------------------------------------------------ */
  --canvas:      #e8e7df;  /* unbleached cotton duck */
  --canvas-lift: #f2f1ea;  /* the slack of the cloth, one shade up */
  --ink:         #22261f;  /* black lino ink, warm-black leaning green */
  --ink-soft:    #4d5347;
  --sage:        #abb19a;  /* sampled from the logo circle */
  --olive:       #5e6650;  /* the webbing straps */
  --seal:        #881c15;  /* the oxblood tag */
  --thread:      #6f7763;
  --hole:        #b4b6a6;
  --rule:        #cdcdc0;

  /* --- type ------------------------------------------------------------- */
  --display: "Alegreya Sans", ui-sans-serif, system-ui, sans-serif;
  --body:    "Alegreya", ui-serif, Georgia, serif;

  --step--1: clamp(0.82rem, 0.79rem + 0.14vw, 0.9rem);
  --step-0:  clamp(1rem, 0.96rem + 0.22vw, 1.12rem);
  --step-1:  clamp(1.18rem, 1.09rem + 0.42vw, 1.44rem);
  --step-2:  clamp(1.4rem, 1.2rem + 0.95vw, 2rem);
  --step-3:  clamp(1.8rem, 1.4rem + 1.9vw, 3rem);
  --step-4:  clamp(2.2rem, 1.5rem + 3.4vw, 4.4rem);

  /* --- rhythm ----------------------------------------------------------- */
  --gap-2:  0.5rem;
  --gap-3:  0.875rem;
  --gap-4:  1.5rem;
  --gap-5:  2.5rem;
  --gap-6:  clamp(3rem, 2rem + 5vw, 5.5rem);
  --gap-7:  clamp(4.5rem, 3rem + 7vw, 8rem);

  --shell:  1120px;
  --edge:   clamp(1.25rem, 4vw, 3rem);
  --ease:   cubic-bezier(0.22, 0.68, 0.32, 1);

  /* 2.7s to sew and tie off + 15s at rest + 1s to fade out = one full loop. */
  --seam-cycle: 18.7s;
}

/* The dark ground is a deepened version of the same ink, not an inversion.
   Sage and seal both have to be lifted or they collapse into the background. */
@media (prefers-color-scheme: dark) {
  :root {
    --canvas:      #14170f;
    --canvas-lift: #1d2118;
    --ink:         #e6e5da;
    --ink-soft:    #a8ab99;
    --sage:        #bfc6ac;
    --olive:       #9aa286;
    --seal:        #d4645a;
    --thread:      #98a086;
    --hole:        #4a4f40;
    --rule:        #333828;
  }
}

:root[data-theme="dark"] {
  --canvas:      #14170f;
  --canvas-lift: #1d2118;
  --ink:         #e6e5da;
  --ink-soft:    #a8ab99;
  --sage:        #bfc6ac;
  --olive:       #9aa286;
  --seal:        #d4645a;
  --thread:      #98a086;
  --hole:        #4a4f40;
  --rule:        #333828;
}

:root[data-theme="light"] {
  --canvas:      #e8e7df;
  --canvas-lift: #f2f1ea;
  --ink:         #22261f;
  --ink-soft:    #4d5347;
  --sage:        #abb19a;
  --olive:       #5e6650;
  --seal:        #881c15;
  --thread:      #6f7763;
  --hole:        #b4b6a6;
  --rule:        #cdcdc0;
}

/* --------------------------------------------------------------------------
   Base
   -------------------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--canvas);
  color: var(--ink);
  font-family: var(--body);
  font-size: var(--step-0);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img { max-width: 100%; display: block; }

h1, h2 { margin: 0; font-family: var(--display); font-weight: 800; line-height: 1.05; text-wrap: balance; }
p { margin: 0; }
ul { margin: 0; padding: 0; list-style: none; }

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

.shell {
  width: min(100% - var(--edge) * 2, var(--shell));
  margin-inline: auto;
}

.skip-link {
  position: absolute;
  left: var(--edge);
  top: 0;
  transform: translateY(-120%);
  z-index: 20;
  padding: 0.6rem 1rem;
  background: var(--ink);
  color: var(--canvas);
  font-family: var(--display);
  text-decoration: none;
  transition: transform 0.2s var(--ease);
}
.skip-link:focus-visible { transform: translateY(0.5rem); }

/* --------------------------------------------------------------------------
   Shared marks
   -------------------------------------------------------------------------- */

.eyebrow {
  font-family: var(--display);
  font-weight: 500;
  font-size: var(--step--1);
  letter-spacing: 0.19em;
  text-transform: uppercase;
  color: var(--olive);
  /* The Greek eyebrow is long enough to wrap on a phone; balancing it keeps a
     single word from being stranded on its own line. */
  text-wrap: balance;
}

.section-title { font-size: var(--step-2); letter-spacing: -0.015em; }

/* The page's one structural device: a run of stitches. Dividers are seams
   already closed; the hero's seam is the one still being sewn. */
.stitch-rule {
  height: 2px;
  border: 0;
  margin: 0;
  background: repeating-linear-gradient(to right,
              var(--rule) 0 14px, transparent 14px 24px);
}

.link {
  color: var(--ink);
  font-family: var(--display);
  font-weight: 500;
  text-decoration: none;
  padding-bottom: 2px;
  background: repeating-linear-gradient(to right, var(--thread) 0 6px, transparent 6px 10px)
              bottom left / 100% 1.5px no-repeat;
  transition: background-size 0.35s var(--ease), color 0.2s var(--ease);
}
.link:hover { color: var(--seal); background-size: 100% 1.5px; }

/* --------------------------------------------------------------------------
   Masthead
   -------------------------------------------------------------------------- */

.masthead { padding-block: clamp(1.5rem, 3vw, 2.5rem); }

.masthead__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap-4);
}

.masthead__mark { display: block; width: clamp(130px, 18vw, 180px); }
.masthead__mark img { width: 100%; height: auto; }

/* The logo art is dark line work on transparency; on the dark ground it has to
   be flipped or it disappears into the page. */
@media (prefers-color-scheme: dark) {
  .masthead__mark img { filter: invert(1) hue-rotate(180deg) brightness(1.12); }
}
:root[data-theme="dark"] .masthead__mark img { filter: invert(1) hue-rotate(180deg) brightness(1.12); }
:root[data-theme="light"] .masthead__mark img { filter: none; }

.lang { display: flex; align-items: center; gap: var(--gap-2); }

/* Each language is its own URL, so these are an anchor and, for the page you
   are already on, a plain span rather than a dead link to itself. */
.lang__btn {
  padding: 0.25rem 0.1rem;
  color: var(--ink-soft);
  font-family: var(--display);
  font-weight: 500;
  font-size: var(--step--1);
  letter-spacing: 0.14em;
  text-decoration: none;
  transition: color 0.2s var(--ease);
}
a.lang__btn:hover { color: var(--ink); }
.lang__btn[aria-current="true"] {
  color: var(--ink);
  font-weight: 800;
  background: repeating-linear-gradient(to right, var(--seal) 0 5px, transparent 5px 9px)
              bottom left / 100% 2px no-repeat;
}
.lang__sep { color: var(--rule); }

/* --------------------------------------------------------------------------
   Hero — the unfinished seam
   -------------------------------------------------------------------------- */

.hero { padding-block: clamp(2.5rem, 6vw, 5rem) var(--gap-7); }

.hero__title {
  margin-top: var(--gap-3);
  font-size: var(--step-4);
  letter-spacing: -0.025em;
  max-width: 15ch;
}

.seam {
  position: relative;
  height: 12px;
  margin-top: clamp(2rem, 4vw, 3rem);
  max-width: 460px;
}

.seam__holes,
.seam__thread {
  position: absolute;
  inset-block: 5px;
  inset-inline: 0;
  height: 2px;
}

/* The part not yet sewn: pin pricks in the cloth, waiting for thread. */
.seam__holes {
  background: repeating-linear-gradient(to right,
              var(--hole) 0 2px, transparent 2px 24px);
}

.seam__thread {
  background: repeating-linear-gradient(to right,
              var(--thread) 0 14px, transparent 14px 24px);
  clip-path: inset(0 100% 0 0);
  animation: sew var(--seam-cycle) var(--ease) 0.35s infinite;
}

.seam__knot {
  position: absolute;
  top: 50%;
  left: 62%;
  width: 8px;
  height: 8px;
  margin: -4px 0 0 -4px;
  border-radius: 50%;
  background: var(--seal);
  opacity: 0;
  transform: scale(0.2);
  animation: knot var(--seam-cycle) var(--ease) 0.35s infinite;
}

/* One cycle: sew for 2.2s, tie the knot by 2.7s, rest 15s, then fade the thread
   out over the last second and reset. Thread and knot share the duration and
   delay so they can never drift apart. Percentages are of --seam-cycle:
   2.2s = 11.8%   2.7s = 14.4%   17.7s = 94.7% */
@keyframes sew {
  0%     { clip-path: inset(0 100% 0 0); opacity: 1; }
  11.8%  { clip-path: inset(0 38% 0 0); opacity: 1; }
  94.7%  { clip-path: inset(0 38% 0 0); opacity: 1; }
  100%   { clip-path: inset(0 38% 0 0); opacity: 0; }
}

/* Restarting at opacity 0 / scale 0.2 is invisible either way, so the wrap
   from the last keyframe back to the first is never seen. */
@keyframes knot {
  0%, 11.8% { opacity: 0; transform: scale(0.2); }
  14.4%     { opacity: 1; transform: scale(1); }
  94.7%     { opacity: 1; transform: scale(1); }
  100%      { opacity: 0; transform: scale(1); }
}

.hero__status {
  margin-top: var(--gap-3);
  font-family: var(--display);
  font-weight: 500;
  font-size: var(--step--1);
  letter-spacing: 0.19em;
  text-transform: uppercase;
  color: var(--seal);
}

.hero__lede {
  margin-top: var(--gap-5);
  max-width: 56ch;
  font-size: var(--step-1);
  color: var(--ink-soft);
}

.hero__cue { margin-top: var(--gap-4); }

/* --------------------------------------------------------------------------
   Gallery
   -------------------------------------------------------------------------- */

.work { padding-bottom: var(--gap-7); }

.section-head {
  display: flex;
  align-items: baseline;
  gap: var(--gap-4);
  margin-bottom: var(--gap-5);
}
.section-head .stitch-rule { flex: 1; align-self: center; }

/* Deliberately not a uniform tile grid: the collection shot opens full width,
   then the singles fall 2 / 3 / 2 so the page reads as a lookbook spread. */
.gallery {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--gap-4);
}

/* Large phones and tablets: two up, every bag the same size. The 2/3/2 rhythm
   below needs real width to work — forced in this early it squeezes the
   three-up row to about 200px, too small to read a hand-carved print. */
@media (min-width: 640px) {
  .gallery { grid-template-columns: repeat(2, 1fr); gap: clamp(1rem, 2vw, 1.5rem); }
  .tote--plate { grid-column: span 2; }
}

/* Seven singles across two columns strand the last one alone on its own row.
   Letting it run full width closes the grid and echoes the opening plate.
   Bounded above, because past 1000px the 2/3/2 rhythm fills the rows itself —
   and :last-child would otherwise outrank .tote--wide and break that layout. */
@media (min-width: 640px) and (max-width: 999.98px) {
  .gallery .tote:last-child { grid-column: span 2; }
}

/* Wide enough for the lookbook spread: collection shot across the top,
   then the singles fall 2 / 3 / 2. */
@media (min-width: 1000px) {
  .gallery { grid-template-columns: repeat(6, 1fr); }
  .tote--plate { grid-column: span 6; }
  .tote--wide  { grid-column: span 3; }
  .tote--third { grid-column: span 2; }
}

.tote { margin: 0; }

.tote__btn {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: var(--canvas-lift);
  cursor: zoom-in;
  overflow: hidden;
  position: relative;
}

/* Blur-up: the 20px stand-in from placeholders.css holds the frame until the
   photograph arrives, so nothing reflows and nothing flashes empty. */
.tote__btn::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: var(--placeholder);
  background-size: cover;
  filter: blur(12px);
  transform: scale(1.1);
  transition: opacity 0.5s var(--ease);
}
.tote.is-loaded .tote__btn::before { opacity: 0; }

.tote__btn img {
  position: relative;
  width: 100%;
  height: auto;
  aspect-ratio: 2048 / 1365;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.6s var(--ease), transform 0.7s var(--ease);
}
.tote.is-loaded .tote__btn img { opacity: 1; }
.tote__btn:hover img { transform: scale(1.03); }

/* Scroll reveal — the class is only ever added when motion is welcome. */
.tote { opacity: 1; }
@media (prefers-reduced-motion: no-preference) {
  .js .tote {
    opacity: 0;
    transform: translateY(18px);
    transition: opacity 0.7s var(--ease), transform 0.7s var(--ease);
  }
  .js .tote.is-revealed { opacity: 1; transform: none; }
}

.work__note {
  margin-top: var(--gap-5);
  max-width: 48ch;
  color: var(--ink-soft);
  font-size: var(--step--1);
}

/* --------------------------------------------------------------------------
   Contact
   -------------------------------------------------------------------------- */

.contact { padding-bottom: var(--gap-7); }
.contact .section-title { margin-top: var(--gap-6); }
.contact__lede { margin-top: var(--gap-3); max-width: 52ch; color: var(--ink-soft); }

.channels {
  display: grid;
  gap: var(--gap-3);
  margin-top: var(--gap-5);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
}

.channel__link {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 0 var(--gap-3);
  padding: var(--gap-3) var(--gap-4);
  background: var(--canvas-lift);
  color: var(--ink);
  text-decoration: none;
  transition: background 0.25s var(--ease), transform 0.25s var(--ease);
}
.channel__link:hover { background: color-mix(in oklab, var(--sage) 34%, var(--canvas-lift)); transform: translateY(-2px); }

.channel__icon {
  grid-row: span 2;
  width: 26px;
  height: 26px;
  fill: none;
  stroke: var(--olive);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.channel__link:hover .channel__icon { stroke: var(--seal); }

.channel__label {
  font-family: var(--display);
  font-weight: 500;
  font-size: var(--step--1);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--olive);
}
.channel__value { font-size: var(--step-0); line-height: 1.3; }

/* --------------------------------------------------------------------------
   Footer
   -------------------------------------------------------------------------- */

.footer { border-top: 1px solid var(--rule); padding-block: var(--gap-5); }

.footer__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--gap-2) var(--gap-4);
}

.footer__mark {
  font-family: var(--display);
  font-weight: 800;
  letter-spacing: 0.02em;
}
.footer__note { color: var(--ink-soft); font-size: var(--step--1); }

/* --------------------------------------------------------------------------
   Viewer — native <dialog>, so focus trapping and Esc come for free
   -------------------------------------------------------------------------- */

/* The dialog fills the viewport and centres the photograph inside it, rather
   than shrink-wrapping the photograph and hanging the controls off its corners.
   Shrink-wrapping breaks on a landscape phone: the image fills the height, so
   there is no room outside it and the close button lands off-screen. */
.viewer {
  inset: 0;
  width: 100vw;
  max-width: 100vw;
  height: 100dvh;
  max-height: 100dvh;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--ink);
  overflow: hidden;
}

/* A closed <dialog> is display:none, so the centring has to live on [open] —
   setting it in the base rule would make the closed dialog visible. */
.viewer[open] {
  display: grid;
  place-items: center;
}

.viewer::backdrop { background: color-mix(in oklab, #14170f 88%, transparent); }

.viewer__frame {
  display: block;
  max-width: min(94vw, 1400px);
  max-height: 92dvh;
}

.viewer__img {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 92dvh;
  object-fit: contain;
}

/* Anchored to the viewport, not to the photograph, so they are reachable at any
   window shape. They now sit over the image, hence the shadow to keep them
   legible against a pale canvas bag as well as a dark one. */
.viewer__close,
.viewer__nav {
  position: fixed;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: 50%;
  background: color-mix(in oklab, #e8e7df 88%, transparent);
  color: #22261f;
  cursor: pointer;
  box-shadow: 0 2px 10px rgb(0 0 0 / 0.3);
  transition: background 0.2s var(--ease), transform 0.2s var(--ease);
}
.viewer__close:hover,
.viewer__nav:hover { background: #e8e7df; transform: scale(1.06); }

.viewer__close svg,
.viewer__nav svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* env() keeps them clear of a notch, which in landscape eats a side rather than
   the top. Vertical centring uses `translate` so the hover `transform` on the
   shared rule above cannot cancel it out. */
.viewer__close {
  top: max(0.75rem, env(safe-area-inset-top, 0px));
  right: max(0.75rem, env(safe-area-inset-right, 0px));
}
.viewer__nav--prev {
  top: 50%;
  translate: 0 -50%;
  left: max(0.75rem, env(safe-area-inset-left, 0px));
}
.viewer__nav--next {
  top: 50%;
  translate: 0 -50%;
  right: max(0.75rem, env(safe-area-inset-right, 0px));
}

@media (prefers-reduced-motion: no-preference) {
  .viewer[open] { animation: viewer-in 0.28s var(--ease); }
  @keyframes viewer-in {
    from { opacity: 0; transform: scale(0.985); }
  }
}

/* --------------------------------------------------------------------------
   Motion opt-out — one place, applied last
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }

  /* The seam still has to end up half-sewn — that is the message, not
     decoration. The loop is switched off by name rather than shortened, so it
     cannot come to rest on the faded-out keyframe that ends each cycle. */
  .seam__thread { animation: none; clip-path: inset(0 38% 0 0); opacity: 1; }
  .seam__knot { animation: none; opacity: 1; transform: scale(1); }
}
