/* Display headings. Instrument Sans is loaded as a variable font (Google Fonts:
   weight 400-700, width 75-100%), so the condensed cut headings used to get
   from a separate single-weight .ttf now comes from the width axis. */
.font-instrument {
  font-family: "Instrument Sans", ui-sans-serif, sans-serif;
  font-stretch: 75%;
  font-weight: 700;
}

/* Safety zone overlay for print preview */
.safety-zone {
  position: absolute;
  top: 3%;
  left: 3%;
  right: 3%;
  bottom: 3%;
  border: 2px dashed #22c55e;
  z-index: 10;
  pointer-events: none;
}

/* Subtle zoom animation for hero background */
@keyframes subtle-zoom {
  0% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.08);
  }
  100% {
    transform: scale(1);
  }
}

.animate-subtle-zoom {
  animation: subtle-zoom 20s ease-in-out infinite;
}

/* Respect user's reduced motion preference */
@media (prefers-reduced-motion: reduce) {
  .animate-subtle-zoom {
    animation: none;
  }

  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* Press animation for buttons */
button,
[type="button"],
[type="submit"],
a[class*="bg-"] {
  transition: transform 100ms cubic-bezier(0.2, 0, 0.2, 1);
}

button:active,
[type="button"]:active,
[type="submit"]:active,
a[class*="bg-"]:active {
  transform: scale(0.98);
}

/* Escape hatch from the site-wide border-radius:0 rule. Circular controls that
   overlay imagery (slider arrows, the lightbox close button) read as buttons
   only when they're round, so they opt back in explicitly. !important is
   required to outrank the global rule, which is itself !important. */
.slider-arrow {
  border-radius: 9999px !important;
}

/* Same escape hatch, for artist portraits. A separate class rather than reusing
   .slider-arrow: these are images, not controls, and overloading that name would
   make the next person think a portrait is a button. Needs a square box to read
   as a circle rather than an ellipse — see the aspect-square on its container. */
.avatar-circle {
  border-radius: 9999px !important;
  overflow: hidden;
}

/* Pill-shape escape hatch. The site is otherwise flat/square (see the global
   border-radius:0 rule below), but a defined set of interactive elements opt
   back in to a full pill: the primary CTAs ("Print your photo", "Continue to
   Payment", form submits), the grey secondary buttons ("See it on your wall"),
   and the header nav links' hover/active background. !important is required to
   outrank the global rule, which is itself !important. Icon/admin/utility
   controls and selection-style pickers stay square. */
.cta-pill {
  border-radius: 9999px !important;
}

/* Primary CTA fill — the single source of truth for the main action colour.
   The site's dark utilities use zinc-900 (#2e2e2e, a soft dark grey); the
   primary action wants a stronger near-black so it clearly reads as THE button.
   Change --cta-bg / --cta-bg-hover here and every primary CTA updates at once.
   Pairs with .cta-pill (shape). Secondary buttons (e.g. the grey AR "See it on
   your wall" button) intentionally do NOT use this. */
:root {
  --cta-bg: #000000;
  --cta-bg-hover: #1a1a1a;
}
.cta-solid {
  background-color: var(--cta-bg);
  color: #ffffff;
}
.cta-solid:hover {
  background-color: var(--cta-bg-hover);
}

/* Rounded corners for the <model-viewer> 3D wall preview. Escape hatch from the
   global border-radius:0 rule (hence !important). overflow:hidden clips the
   canvas — which lives in model-viewer's shadow DOM and can't be reached from
   here — to the rounded host, so the corners actually round. */
.ar-rounded {
  border-radius: 0.75rem !important;
  overflow: hidden;
}

/* Corner radii for the size picker. The grey sleeve (.rounded-sleeve) is larger;
   the rows / selection (.rounded-soft) are smaller by the sleeve's padding, so
   the two curves are concentric — 1rem − 0.375rem (p-1.5) = 0.625rem.
   All escape hatches from the global border-radius:0 rule, hence !important. */
.rounded-sleeve {
  border-radius: 1rem !important;
}
.rounded-soft {
  border-radius: 0.625rem !important;
}
/* Top-only / bottom-only variants, for the first and last rows of a grouped
   card so only the group's outer corners round while middle rows stay square. */
.rounded-soft-t {
  border-top-left-radius: 0.625rem !important;
  border-top-right-radius: 0.625rem !important;
}
.rounded-soft-b {
  border-bottom-left-radius: 0.625rem !important;
  border-bottom-right-radius: 0.625rem !important;
}
/* The selected card's black outline, rounded on all four corners (overrides the
   -t/-b variants; declared after them so it wins the cascade at equal specificity). */
.rounded-select {
  border-radius: 0.375rem !important;
}
/* The selected size already shows a black outline; suppress the grey hover
   border on it so the two don't stack into a double edge on hover. */
.selected-size:hover {
  border-color: transparent !important;
}
/* Size rows round all four corners when hovered or (on the options page's radio
   rows) checked, so the black outline reads as a rounded chip rather than following
   the group's square middle corners. Custom CSS, since .rounded-select can't be a
   Tailwind hover:/peer-checked: variant. */
.size-card:hover,
.peer:checked ~ .size-card {
  border-radius: 0.375rem !important;
}

/* Hide the scrollbar on the scroll-snap image track — the swipe and the progress
   line convey position; a scrollbar would just clutter the artwork. */
.no-scrollbar {
  -ms-overflow-style: none;   /* IE / old Edge */
  scrollbar-width: none;      /* Firefox */
}
.no-scrollbar::-webkit-scrollbar {
  display: none;              /* WebKit / Blink */
}

/* Indeterminate upload bar. Replaces a spinning ring, which relied on
   rounded-full and so rendered as a spinning *square* under the global
   border-radius:0 rule. A sliding bar needs no radius and matches the flat
   language used elsewhere (the gallery slider indicator). The reduced-motion
   block below neutralises this automatically. */
@keyframes upload-indeterminate {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(500%); }
}

.upload-indeterminate-bar {
  animation: upload-indeterminate 1.3s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}

/* The size rows on the product page opt out of the global press animation:
   they are flush-stacked with shared borders, so scaling one on press breaks
   the shared line and makes the whole list jump. */
[data-format-picker-target="option"],
[data-format-picker-target="option"]:active {
  transition: border-color 150ms ease;
  transform: none;
}

/* Accordion headers (gallery "Details", FAQ, footer dropdowns) also opt out of
   the press-scale: the heading shouldn't shift or shrink when you click to
   expand/collapse. */
[data-action*="faq-accordion#toggle"]:active,
[data-action*="footer-accordion#toggle"]:active {
  transform: none;
}

/* Tabular numbers for price displays */
.tabular-nums {
  font-variant-numeric: tabular-nums;
}

/* Premium soft shadow for the category glass cards — diffuse and low-opacity,
   lifting gently on hover. (Tailwind's shadow-* presets read too hard here.) */
.card-premium {
  /* Big negative spread keeps the glow tucked under the card so it doesn't bleed
     up over the top edge — or sideways into the scroll gutter, where the
     horizontal scroller would clip it hard against the viewport edge. */
  box-shadow: 0 2px 4px -2px rgba(17, 24, 39, 0.04), 0 12px 22px -12px rgba(17, 24, 39, 0.06);
  transition: box-shadow 0.4s ease, transform 0.4s ease;
}
.card-premium:hover {
  box-shadow: 0 3px 6px -3px rgba(17, 24, 39, 0.05), 0 16px 34px -16px rgba(17, 24, 39, 0.09);
  transform: translateY(-1px);
}
/* Gentler press than the global a[class*="bg-"]:active scale(0.98). */
.card-premium:active {
  transform: scale(0.99) !important;
}

/* Gradient hairline border that follows the element's border-radius (the padded
   gradient-background trick breaks at rounded corners). A masked 1px ::before
   ring: faint grey along the top, fading to nothing (white) by the bottom. */
.gradient-ring::before {
  content: "";
  position: absolute;
  inset: 0;
  /* !important so it beats the global `*::before { border-radius: 0 !important }`
     flat rule — otherwise the ring is squared off on a rounded card. */
  border-radius: 1rem !important;
  padding: 1px;
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0.04) 0%, rgba(0, 0, 0, 0) 55%);
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
  z-index: 2;
}

/* Hide the scrollbar on horizontal carousels (collection rows) while keeping the
   row scrollable by swipe/trackpad. */
.no-scrollbar {
  -ms-overflow-style: none;
  scrollbar-width: none;
}
.no-scrollbar::-webkit-scrollbar {
  display: none;
}

/* Round the native file-selector button (e.g. the Sell upload box). The global
   flat rule squares it, and a pseudo-element can't carry a utility class, so the
   rounding has to live here. */
.file-rounded::file-selector-button,
.file-rounded::-webkit-file-upload-button {
  border-radius: 9999px !important;
}

/* Windows fix: the dark-styled "Print size" <select> renders its native option
   list white-on-white on Windows browsers (macOS ignores these colors for the
   native menu). Force readable on-theme dark options. Other selects use a light
   background, so this is scoped to the format picker only. */
select[name="format_id"] option {
  color: #ffffff;
  background-color: #18181b;
}

/* Flat design: no rounded corners anywhere. Overrides Tailwind's rounded-*
   utilities, inline styles, and cursor.css in one rule — including markup built
   in JS (e.g. the dashboard side panel). Remove this block to restore radii. */
*,
*::before,
*::after {
  border-radius: 0 !important;
}

/* ─────────────────────────────────────────────────────────────────────────
   Homepage "How it works" — three inline-SVG "stage" demos driven one beat at
   a time by the how_it_works Stimulus controller (a cursor/parcel acts on each
   UI). Tokens are scoped to .hiw; element/state classes are hiw-* prefixed.
   Motion is CSS transitions the controller toggles; it also honours
   prefers-reduced-motion by settling each card on a finished frame. SVG rx/ry
   are unaffected by the global border-radius:0 flat rule.
   ───────────────────────────────────────────────────────────────────────── */
.hiw {
  --ap-ink: #2e2e2e;
  --ap-ink-2: #757575;
  --ap-card: #f0f0f0;
  --ap-paper: #fcfcfc;
  --ap-line: #e0e0e0;
  --ap-ease: cubic-bezier(.22, 1, .36, 1);
  --hiw-cursor-fill: #1c1917;
  --hiw-cursor-stroke: #ffffff;
}

/* Stage = positioning context for the cursor/ripple overlays. */
.hiw-stage { position: relative; }
.hiw-ill { display: block; width: 100%; height: auto; }

.hiw-cursor {
  position: absolute; left: 0; top: 0; width: 22px; height: 22px;
  transform: translate(-2px, -3px);
  transition: left .6s var(--ap-ease), top .6s var(--ap-ease);
  pointer-events: none; z-index: 3;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .28));
  will-change: left, top;
}
.hiw-ripple {
  /* border-radius needs !important to beat the global flat rule
     (*,::before,::after { border-radius: 0 !important }) — without it the
     click ripple renders as a square. */
  position: absolute; left: 0; top: 0; width: 6px; height: 6px; border-radius: 9999px !important;
  border: 2px solid var(--ap-ink); transform: translate(-50%, -50%);
  opacity: 0; pointer-events: none; z-index: 2;
}

/* 1 · Upload */
.hiw-u-photo { transform-box: fill-box; transform-origin: center; transition: transform .7s var(--ap-ease), opacity .4s ease; }
.hiw-u-bar   { transform-box: fill-box; transform-origin: left center; transform: scaleX(0); transition: transform 1s linear; }
.hiw-u-done  { transform-box: fill-box; transform-origin: center; transform: scale(0); opacity: 0; transition: transform .45s cubic-bezier(.34, 1.56, .64, 1), opacity .3s ease; }
.hiw-u-zone  { transition: stroke .3s ease, opacity .3s ease; }
.hiw-u-zone.is-hot { stroke: var(--ap-ink); opacity: .9; }

/* 2 · Options */
.hiw-o-ring, .hiw-o-dot { transform-box: fill-box; transform-origin: center; opacity: 0; transition: transform .45s var(--ap-ease), opacity .3s ease; }
.hiw-o-price { fill: var(--ap-ink-2); transform-box: fill-box; transform-origin: right center; transition: fill .3s ease, transform .3s var(--ap-ease); }
.hiw-o-price.is-on { fill: var(--ap-ink); transform: scale(1.12); }

/* 3 · Delivery */
.hiw-d-cam  { transform-box: view-box; transform-origin: center; transition: transform .6s ease, opacity .4s ease; }
.hiw-d-cam.is-push { transform: scale(1.05); }
.hiw-d-line { transform-box: fill-box; transform-origin: center top; transform: scaleY(0); transition: transform .95s linear; }
.hiw-d-dot  { transform-box: fill-box; transform-origin: center; transform: scale(0); transition: transform .45s cubic-bezier(.34, 1.56, .64, 1); }
.hiw-d-dot.is-on { transform: scale(1); }
.hiw-d-txt  { opacity: .45; transition: opacity .35s ease; }
.hiw-d-txt.is-on { opacity: 1; }
.hiw-d-parcel { transform-box: fill-box; transform-origin: center; transform: translateY(0); opacity: 0; transition: transform .95s linear, opacity .25s ease; }

@media (prefers-reduced-motion: reduce) {
  .hiw-cursor, .hiw-u-photo, .hiw-u-bar, .hiw-u-done, .hiw-o-ring, .hiw-o-dot, .hiw-o-price,
  .hiw-d-cam, .hiw-d-line, .hiw-d-dot, .hiw-d-txt, .hiw-d-parcel { transition: none !important; }
}
/* Branded "ink ring" upload loader. Everything scales from --ink-size (reference
   design: 64px box, 18px dots, radius breathing between 14px and 5px).
   NOTE: the dot/core border-radius carries !important solely to outrank the
   site-wide `* { border-radius: 0 !important }` flat-design rule — without it the
   ink dots render as squares. Timings, easings and colours are unchanged. */
@property --r { syntax: '<number>'; inherits: false; initial-value: 0.21875; }
@property --k { syntax: '<number>'; inherits: false; initial-value: 0; }
@property --s { syntax: '<number>'; inherits: false; initial-value: 0; }

.ink-loader {
  --ink-size: 64px;
  --dot: calc(var(--ink-size) * 0.28125);
  position: relative;
  width: var(--ink-size);
  height: var(--ink-size);
  isolation: isolate;
}
.ink-loader__core,
.ink-loader__dot {
  position: absolute;
  left: calc(50% - var(--dot) / 2);
  top: calc(50% - var(--dot) / 2);
  width: var(--dot);
  height: var(--dot);
  border-radius: 50% !important;
}
.ink-loader__core { background: #000; transform: scale(0); }
.ink-loader__arm {
  position: absolute;
  inset: 0;
  mix-blend-mode: multiply;
  animation: ink-arm 0.9s linear infinite;
}
.ink-loader__dot {
  --k: 0;
  --s: 0;
  animation: ink-breathe 0.9s linear infinite;
  transform: translateX(calc(var(--r) * var(--k) * var(--ink-size))) scale(var(--s));
}

/* Dark variant: additive light mixing, white start and end */
.ink-loader--dark .ink-loader__arm { mix-blend-mode: screen; }
.ink-loader--dark .ink-loader__core { background: #fff; }

@keyframes ink-arm {
  from { transform: rotate(var(--a)); }
  to { transform: rotate(calc(var(--a) + 360deg)); }
}
@keyframes ink-breathe {
  0%, 100% { --r: 0.21875; }
  50% { --r: 0.078125; }
}

/* Intro: solid dot appears, holds, then opens into the ring */
.ink-loader.is-in .ink-loader__dot {
  --k: 1;
  --s: 1;
  transition:
    --s 0.35s cubic-bezier(.22, 1, .36, 1),
    --k 0.8s cubic-bezier(.16, 1, .3, 1) calc(0.5s + var(--i) * 15ms);
}
.ink-loader.is-in .ink-loader__core { animation: ink-core-in 1s forwards; }
@keyframes ink-core-in {
  0% { transform: scale(0); animation-timing-function: cubic-bezier(.22, 1, .36, 1); }
  35%, 50% { transform: scale(1); animation-timing-function: cubic-bezier(.4, 0, 1, 1); }
  80%, 100% { transform: scale(0); }
}

/* Outro: inks converge, hold as a solid dot, then shrink away */
.ink-loader.is-out .ink-loader__dot {
  --k: 0;
  --s: 0;
  transition:
    --k 0.5s cubic-bezier(.65, 0, .35, 1) calc(var(--i) * 15ms),
    --s 0.35s cubic-bezier(.65, 0, .35, 1) 0.7s;
}
.ink-loader.is-out .ink-loader__core { animation: ink-core-out 1.05s forwards; }
@keyframes ink-core-out {
  0%, 52.4% { transform: scale(0); }
  57.1%, 66.7% { transform: scale(1); animation-timing-function: cubic-bezier(.65, 0, .35, 1); }
  100% { transform: scale(0); }
}

@media (prefers-reduced-motion: reduce) {
  .ink-loader__arm, .ink-loader__dot { animation: none; }
  .ink-loader.is-in .ink-loader__dot { --k: 1; --s: 1; transition: --s 0.3s ease-out; }
  .ink-loader.is-out .ink-loader__dot { --k: 1; --s: 0; transition: --s 0.3s ease-in; }
  .ink-loader__core { display: none; }
}
/*
 * This is a manifest file that'll be compiled into application.css, which will include all the files
 * listed below.
 *
 * Any CSS (and SCSS, if configured) file within this directory, lib/assets/stylesheets, or any plugin's
 * vendor/assets/stylesheets directory can be referenced here using a relative path.
 *
 * You're free to add application-wide styles to this file and they'll appear at the bottom of the
 * compiled file so the styles you add here take precedence over styles defined in any other CSS
 * files in this directory. Styles in this file should be added after the last require_* statement.
 * It is generally better to create a new file per style scope.
 *


 */
